[TypeScript] 섹션5. 함수와 타입(1)

jaehoon ahn·2025년 2월 6일

TypeScript

목록 보기
7/14
post-thumbnail

함수 타입

함수 타입 정의

함수를 설명하는 가장 좋은 방법

  1. 어떤 매개변수를 받고, 어떤 결과값을 반환하는지에 대한 이야기
  2. 어떤 [타입의] 매개변수를 받고, 어떤 [타입의] 결과값을 반환하는지

정의 방법


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

화살표 함수의 타입을 정의하는 방법

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

함수의 매개변수

function introduce(name = "제노", tall: number) {
  console.log(`name: ${name}`);
  console.log(`tall: ${tall}`);
}
introduce("제노", 175);

기본값을 string을 넣어줘서 string으로 추론함

선택적 매개변수

function introduce(name = "제노", tall?: number) {
  console.log(`name: ${name}`);
  console.log(`tall: ${tall}`);
}
introduce("제노"); 

tall 매개변수가 인수로 전달하지 않아서 오류 발생, 생략하고 싶다면 tall?:

추가(…연산자)

// ...연산자: 가변 길이의 매개변수를 전달하면 배열로 묶어주는 js 문법
function getSum(...rest: number[]) {
  let sum = 0;
  rest.forEach((it) => (sum += it));
}
getSum(1, 2, 3); // 6

함수 타입 표현식과 호출 시그니처

함수 타입 표현식

이전 강의에서는 다음과 같이 타입을 정의했었다.

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

함수 타입 표현식 사용법

⇒ 타입 별칭을 통해 함수의 타입을 정의할 수 있다.

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

사용 이유

  • 함수 타입 표현식을 사용하지 않으면 각각 타입을 정의해줘야 해서 중복 코드가 많아진다.
  • 이런 상황에서 함수 타입 표현식을 사용하면 중복 코드를 제거할 수 있다.

예시

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;

호출 시그니쳐(콜 시그니쳐)

type Operation2 = {
  (a: number, b: number): number;
};
type Operation2 = {
  (a: number, b: number): number;
};
const add2: Operation2 = (a, b) => a + b;
const sub2: Operation2 = (a, b) => a - b;
const multiply2: Operation2 = (a, b) => a * b;
const divide2: Operation2 = (a, b) => a / b;

함수 타입의 호환성

함수 타입 호환성

⇒ 특정 함수 타입을 다른 함수 타입으로 취급해도 괜찮은가를 판단하는 의미

체크 리스트

  1. 반환값의 타입이 호환되는가
  2. 매개변수의 타입이 호환되는가

기준1. 반환값이 호환되는가

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

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

a = b;  
// number 리터럴 타입을 number 타입으로 취급하겠다는 것은 
// 업캐스팅이므로 호환 가능하다.
b = a; // 반대는 허용이 안됌

b = a가 호환되지 않는 이유

a의 타입은 number, b는 number 리터럴 타입이다

number를 number 리터럴로 취급하겠다는 것은 다운 캐스팅이므로 호환이 되지 않는 것이다.

⇒ 즉, 반환값끼리 다운 캐스팅이 되면 안된다.

기준2. 매개변수가 호환되는가

2-1. 매개변수의 개수가 같을 때

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

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

위 코드에서는 매개변수의 개수가 같고, 타입도 같다.

양쪽으로 다 호환이 가능하다.

호환이 되지 않는 경우

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

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

c = d
// d 타입이 number가 아닌 number 리터럴이면 오류가 발생한다.
// 즉, d 타입을 c타입으로 취급하겠다는 것.
// number 리터럴 타입을 => number 타입으로 취급하겠다는 것은 업캐스팅
// 근데 오류가 발생
// 매개변수의 타입을 기준으로 호환성을 판단할 때는 반대로 
// 업캐스팅일 때는 호환이 안된다고 평가함
d = c
// c 타입을 d 타입으로 취급하겠다는 것
// c는 number타입, d는 number 리터럴 타입
// 이는 다운캐스팅이다.
// 매개변수의 호환성을 판단할 때는 다운캐스팅을 호환할 수 있도록 한다.

매개변수가 객체타입을 사용하는 예시

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);
};
animalFunc = dogFunc; // 호환이 안된다.
// animalFunc 타입이 슈퍼타입이며 업캐스팅이 일어난다.
dogFunc = animalFunc;

매개변수 호환이 되지 않았던 상황에 대한 이유

let testFunc = (animal: Animal) => {
  console.log(animal.name);
  console.log(animal.color); // animal에는 프로퍼티가 없음
  // 이런 말도 안되는 코드가 나올 수 있기 때문에, 
  // 다운캐스팅을 허용하는 것임
};
let testFun2 = (dog: Dog) => {
  console.log(dog.name); // 호환 가능
  console.log(dog.color); // 호환 가능
};

⇒ 즉, 정리하자면 매개변수 기준으로 호환성을 따질 때는, 다운 캐스팅만 호환이 된다는 것이다.

2-2. 매개변수의 개수가 다를 때

type Func1 = (a: number, b: number) => void;
type Func2 = (a: number) => void;

let func1: Func1 = (a, b) => {};
let func2: Func2 = (a) => {};

func1 = func2;
func2 = func1; // 호환 불가

// func1 매개변수 갯수는 2개, func2는 1개
// func1(2개) <- func2(1개)
// 매개변수가 다를 때는 할당하려고 하는 쪽의 함수의 타입의 
// 매개변수의 갯수가 더 적을 때만 호환이 된다.
// 또한 타입이 같은 매개변수가 있어야 한다.

0개의 댓글