function getSum(...rest: number[]) {
let sum = 0;
rest.forEach((it) => (sum += it));
return sum;
}
getSum(1, 2, 3, 4, 5);
// 타입 갯수 제한
function getSum(...rest: [number, number, number]) {
let sum = 0;
rest.forEach((it) => (sum += it));
return sum;
}
// 매개변수 3개까지만 받을 수 있음
getSum(1, 2, 3);
함수 타입 표현식과 호출 시그니쳐
함수 타입 표현식 (Function Type Expression)
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;
const add2: (a: number, b: number) => number = (a, b) => a + b;
중복될 경우, 함수타입 표현식을 이용하여 공용적으로 사용할 수 있다.
호출 시그니쳐(콜 시그니쳐)
type Operation2 = {
(a: number, b: number): number;
};
const add: Operation2 = (a, b) => a + b;
const sub: Operation2 = (a, b) => a - b;
const multiply: Operation2 = (a, b) => a * b;
const divide: Operation2 = (a, b) => a / b;
하이브리드 타입
호출 시그니쳐 아래에 프로퍼티를 추가 정의한 경우
type Operation2 = {
(a: number, b: number): number;
name: string;
};
const add2: Operation2 = (a, b) => a + b;
(...)
add2(1, 2);
add2.name;
함수 타입의 호환성
특정 함수 타입을 다른 함수 타입으로 취급해도 괜찮은가를 판단하는 것
반환값의 타입이 호환되는지
매개변수의 타입이 호환되는지
두 가지의 기준이 함수타입에 호환된다고 볼 수 있음.
반환값의 타입이 호환되는 경우
type A = () => number; // return number type
type B = () => 10; // return number literal
let a: A = () => 10;
let b: B = () => 20; // type Error
let c: B = () => 10; // type number literal
a = c;
c = a; // 다운 캐스팅은 안됨.
A 반환값 타입이 B 반환값 타입의 슈퍼타입이라면 두 타입은 호환됨.
매개변수의 타입이 호환되는 경우
매개변수의 개수가 같을 떄
type C = (value: number) => void;
type D = (value: number) => void;
type E = (value: 10) => void;
let c: C = (value) => {};
let d: D = (value) => {};
c = d;
d = c;
c = e; // type Error
매개변수를 기준으로 타입을 평가할 때에는 다운 캐스팅은 허용되고, 업캐스팅은 허용되지 않음
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; // TypeError
객체 개념으로 생각하면, 이해하기가 쉽다.
매개변수의 개수가 다를 때
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; // Type Error
타입이 같은 매개변수가 있을 때
할당하려고하는 쪽은 매개변수의 갯수가 더 적을 때에만 호환이 된다.
함수 오버로딩
함수를 매개변수의 개수나 타입에 따라 여러가지 버전으로 정의하는 방법
// 오버로드 시그니처
function func(a: number): void; // version 1
function func(a: number, b: number, c: number): void; // version 2
// 구현 시그니처
function func() {}
func(); // typeError
func(1); // 오버로드 시그니처 1번 버전과 맞음
func(1, 2); // typeError
func(1, 2, 3); // 오버로드 시그니처 2번 버전과 맞음
오버로드 시그니처를 가지고 있으면, 함수를 호출할 때 인수들의 타입이 실제 구현부에 정의된 매개변수의 갯수를 따르지 않고, 오버로드 시그니처를 따라감.
// 오버로드 시그니처
function func(a: number): void; // version 1
function func(a: number, b: number, c: number): void; // version 2
// 구현 시그니처
function func(a : number, b : number; c : number) {}
이렇게 사용했을 때, 오버로드 시그니처 version 1이 오류가 발생한다. 왜냐하면 구현 시그니처에서 매개변수 3개를 필수 매개변수로 정의함에 따라, 첫번째 오버로드 시그니처의 의미가 없어지도록 만들어지게 된다.
// 구현 시그니처
function func(a : number, b ?: number; c ?: number) {
if(typeof b === 'number' && typeof c === 'number') {
return console.log(a + b + c)
}
console.log(a * 20)
}
그래서, 매개변수들을 옵셔널로 만들어 모든 오버로드 시그니처가 의미가 있도록 만들어 줘야 한다.