[TIL] 241007_Typescript: 함수 타입 익히기

지코·2024년 10월 7일
0

Today I Learned

목록 보기
29/66
post-thumbnail

⚡️ 함수 타입

타입스크립트에서 기본적으로 함수를 선언할 때의 형태를 알아보자.

function func(a: number, b: number): number {
  return a + b;
}

함수의 타입은 함수의 반환값의 타입과 같다.
보통 이와 같이 각 매개변수에 타입을 지정해주면, 타입 추론 덕에 함수의 타입도 알아서 지정된다.

같은 함수를 화살표 함수로 정의하면 다음과 같다.

const add = (a: number, b: number): number => a + b;

여기서 짚고 넘어가야 할 세 가지 규칙이 있다.

1️⃣ 매개변수에 기본값을 설정할 경우, 타입을 지정해주지 않아도 기본적으로 타입이 잘 추론된다.
하지만 매개변수의 기본값의 타입과 함수 호출 시 사용한 인수의 타입이 다를 경우 오류가 발생한다.

2️⃣ optional로 설정한 매개변수의 타입은 선언한 타입과 undefinedunion 타입이 된다.

그래서 위와 같이 typeof 연산자를 사용한 타입 가드를 통해, 예외 처리를 해줘야 한다.

3️⃣ 필수 매개변수는 선택적(optional) 매개변수보다 앞에 있어야 한다.
필수 매개변수 - 선택적 매개변수 - 필수 매개변수와 같은 순서로 선언하면 안된다는 뜻!

⚡️ 함수 타입 표현식

이 경우는 예제 코드를 보면 이해가 빠르다.

type Operation = (a: number, b: number) => number;

const add: Operation = (a, b) => a + b;
const sub: Operation = (a, b) => a - b;
const multiply: Operation = (a, b) => a * b;
const divide: Operation = (a, b) => a / b;

위와 같이 연산 관련된 함수를 여러 개 작성할 경우 매번 타입 선언을 하면 귀찮기도 하고, 코드의 양도 방대해진다.
따라서 타입 별칭을 사용하면, 타입 선언을 한 번에 처리할 수 있다.

⚡️ 함수 오버로딩

함수 오버로딩이란?
하나의 함수를 매개변수의 개수나 타입에 따라 여러 가지 버전으로 만드는 문법.

타입스크립트에서는 함수 형태 선언과 실제 구현을 따로 분리한다.
예제 코드를 보자.

// 오버로드 시그니처
function func(a: number): void;
function func(a: number, b: number, c: number): void;

// 실제 구현부 = 구현 시그니처
// 오버로딩하는 함수들에 따라 선택적 매개변수를 적용할 것.
function func(a: number, b?: number, c?: number) {
  if (typeof b === "number" && typeof c === "number")
    console.log(a + b + c);
  else
    console.log(a * 20);
}

func(1);
func(1, 2, 3);

매개변수의 개수에 따라 다른 함수의 기능을 구현한다고 하자.
오버로드 시그니처 선언을 통해 매개변수가 1개일 때와 3개일 때를 구분한다고 먼저 알리고, 아래에 구현 시그니처를 선언한다.
이 때 적절하게 optional 연산자를 사용해서 매개변수를 선언한다.
따라서 함수 실행 시 위와 같이 인수가 한 개일 때와 세 개일 때 모두 에러 없이 작동된다.

⚡️ 함수 타입의 호환성

타입스크립트의 타입들에는 슈퍼 타입서브 타입 관계가 존재한다. 함수 타입에서도 호환성을 따질 때 이와 같은 관계를 체크한다.

1️⃣ 반환값이 호환되는가.

type A = () => number;  // number
type B = () => 10;  // number literal

let a: A = () => 10;
let b: B = () => 10;

a = b;  // 업캐스팅 (O)
// b = a; // 다운캐스팅 (X)

함수 a는 type A로 선언된 함수로 number 타입이고, 함수 b는 type B로 선언된 함수로 number literal 타입이다.

두 함수 모두 10을 반환하는데, b를 a에 할당하는 것은 number literal typenumber type에 할당하는 것이므로 업캐스팅에 해당돼 가능하다.

반대로 a를 b에 할당하는 것은 number typenumber literal type에 할당하는 것이므로 다운캐스팅에 해당돼 불가능하다.

2️⃣ 매개변수가 호환되는가.

type C = (value: number) => void;
type D = (value: 10) => void;

let c: C = (value) => {};
let d: D = (value) => {};

// c = d; // 업캐스팅 (X)
d = c;  // 다운캐스팅 (O)

매개변수에 따라 호환성을 체크하는 것은 반환값에 따라 호환성을 체크하는 것과 정반대이다.

d를 c에 할당하는 것은 number literal typenumber type에 할당하는 것이므로 업캐스팅에 해당되는데, 이는 불가능하다.

이를 객체로 설명하면 다음과 같다.

type Animal = {
  name: string;
};

type Dog = {
  name: string;
  color: string;
};

let animalFunc = (animal: Animal) => {
  console.log(animal.name);
};

let dogFunc = (dog: Dog) => {
  console.log(dog.name);
  console.log(dog.color);
};
// Ex
// animalFunc = dogFunc;	// 불가능
let testFunc = (animal: Animal) => {
  console.log(animal.name);
  //console.log(animal.color);
};

Ex 라인을 실행했을 때 에러가 발생한다. 이는 아래 testFunc()를 확인하면 이해할 수 있다. Ex 라인은 color property를 가지고 있지 않은 animal 객체에게서 color property를 호출하는 꼴이기 때문이다.

따라서 반대의 경우에는 다운캐스팅으로 가능하다.
이는 반복학습을 통해 더 확실히 습득해야 할 부분이다❗️

Reference

👩🏻‍🏫 한입 크기로 잘라 먹는 타입스크립트(TypeScript)

https://www.inflearn.com/course/한입-크기-타입스크립트

profile
꾸준하고 성실하게, FE 개발자 역량을 키워보자 !

0개의 댓글