함수 오버로드
몇몇 자바스크립트 함수는 다양한 인수의 개수, 타입을 통해 호출될 수 있다. (eg. Date를 생성하고 인수로 타임스탬프 하나만 받을 수도 있고 월, 일, 연도를 받는 함수를 만들 수도 있다.)
타입스크립트에서는 이를 오버로드 시그니처를 작성함으로써 묘사할 수 있다. 그러기 위해 함수 시그니처 몇 개(보통 2개 이상)를 적은 후 함수 본문을 작성하면 된다.
function makeDate(timestamp: number): Date; // 오버로드 시그니처 1
function makeDate(m: number, d: number, y: number): Date; // 오버로드 시그니처 2
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { // 구현 시그니처
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3); // ERROR!
// No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.
위 예시에서 두 개의 오버로드를 작성했다. 하나는 한 개의 인수를 받고, 다른 하나는 인수 세 개를 받는다. 처음에 쓴 두 시그니처들을 오버로드 시그니처라고 한다.
그리고 그 아래에 호환 가능한 시그니처와 함께 함수 구현을 작성했는데, 함수는 구현 시그니처를 가지고 있지만 이 시그니처는 직접 호출될 수 없다. 필수적인 매개변수 뒤에 두 개의 선택적 매개변수만으로는 이 함수를 호출할 수 없다.
오버로드 시그니처와 구현 시그니처
종종 아래처럼 작성하고 왜 에러가 발생하는지 이해하지 못하는 경우가 있다.
function fn(x: string): void;
function fn() {
// ...
}
// 0개의 인자로 호출하기를 예상했음
fn(); // ERROR
// Expected 1 arguments, but got 0.
함수 본문을 작성하기 위해 사용된 구현 시그니처는 외부에서 보이지 않는다. 오버로드된 함수를 작성할 때, 두 개 이상의 시그니처를 구현 위에 작성해야 한다.
또한, 구현 시그니처는 오버로드된 시그니처와 호환되어야 한다. 아래의 함수들은 구현 시그니처가 오버로드들과 올바르게 일치하지 않기 때문에 오류가 있다.
function fn(x: boolean): void;
// 인수 타입이 옳지 않습니다.
function fn(x: string): void; // This overload signature is not compatible with its implementation signature.
function fn(x: boolean) {}
function fn(x: string): string;
// 반환 타입이 옳지 않습니다.
function fn(x: number): boolean; // This overload signature is not compatible with its implementation signature.
function fn(x: string | number) {
return "oops";
}
좋은 오버로드 작성하기
제네릭처럼, 함수 오버로드를 작성할 때 따라야 할 몇몇 가이드라인이 있다.
문자열 혹은 배열의 길이를 반환하는 함수를 생각해보자.
function len(s: string): number;
function len(arr: any[]): number;
function len(x: any) {
return x.length;
}
우리는 이 함수를 문자열이나 배열을 통해 호출할 수 있다.
len(""); // OK
len([0]); // OK
하지만 타입스크립트는 하나의 오버로드를 통해서만 함수를 해석하기 때문에 이 함수를 문자열 또는 배열이 될 수 있는 값(string | any[])을 통해 호출할 수 없다.
len(Math.random() > 0.5 ? "hello" : [0]);
/* No overload matches this call.
Overload 1 of 2, '(s: string): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'string'.
Type 'number[]' is not assignable to type 'string'.
Overload 2 of 2, '(arr: any[]): number', gave the following error.
Argument of type 'number[] | "hello"' is not assignable to parameter of type 'any[]'.
Type 'string' is not assignable to type 'any[]'. */
두 오버로드 모두 같은 인수 개수와 같은 반환 타입을 가지기 때문에 오버로드되지 않은 함수의 형태로 작성할 수 있다. 이렇게 하면 호출자는 이 함수를 두 가지 값 중 하나를 이용해 호출할 수 있고, 추가적으로 정확한 구현 시그니처를 찾을 필요도 없어졌다.
function len(x: any[] | string) {
return x.length;
}
가능하다면 오버로드 대신 유니온 타입(|)을 사용하자.
알아야 할 다른 타입
void
void는 값을 반환하지 않는 함수의 반환 값을 의미한다. 함수에 return 문이 없거나, 명시적으로 값을 반환하지 않을 때 추론되는 타입이다.
자바스크립트에서는 아무것도 반환하지 않는 함수는 암묵적으로 undefined 값을 반환한다. 하지만 타입스크립트에서 void와 undefined는 같은 것으로 간주되지 않는다.
object
object는 원시값(string, number, bigint, boolean, symbol, null, undefined)이 아닌 모든 값을 지칭한다. 이것은 빈 객체 타입 {}와는 다르고, 전역 타입 Object와도 다르다.
자바스크립트에서 함수 값은 객체이다. 프로퍼티가 있고, 프로토타입 체인에 Object.prototype이 있고, instanceof Object이면서 Object.keys를 호출할 수 있는 등 여러 이유로 타입스크립트에서 함수 타입은 object로 간주된다.
unknown
unknown 타입은 모든 값을 나타낸다. any 타입과 유사하지만 unknown 타입에 어떤 것을 대입하는 것이 유효하지 않기 때문에 더 안전하다.
function f1(a: any) {
a.b(); // OK
}
function f2(a: unknown) {
a.b(); // ERROR
// Object is of type 'unknown'.
}
이는 any 형태의 값을 함수 본문에 쓰지 않고도 아무 값이나 받는 함수를 표현할 수 있기 때문에 함수 타입을 설명하는 데에 유용하게 쓰인다.
unknown 타입의 값을 반환하는 함수를 표현할 수도 있다.
function safeParse(s: string): unknown {
return JSON.parse(s);
}
// 'obj'를 사용할 때 조심해야 합니다!
const obj = safeParse(someRandomString);
never
never 타입은 결코 관측될 수 없는 값을 의미한다. 반환 타입에서는 해당 함수가 예외를 발생시키거나, 프로그램 실행을 종료함을 의미한다.
never은 타입스크립트가 유니온에 아무것도 남아있지 않다고 판단했을 때 또한 나타난다.
function fn(x: string | number) {
if (typeof x === "string") {
// do something
} else if (typeof x === "number") {
// do something else
} else {
x; // 'never' 타입이 됨!
}
}
Function
전역 타입 Function은 bind, call, apply, 자바스크립트 함수 값에 있는 다른 프로퍼티를 설명하는 데에 사용된다. Function 타입의 값은 언제나 호출될 수 있다는 값을 가지며, 이런 호출은 any를 반환한다.
function doSomething(f: Function) { // function doSomething(f: Function): any
return f(1, 2, 3);
}
이는 타입되지 않은 함수 호출이고, 안전하지 않은 any 타입을 반환하기 때문에 일반적으로 피하는 것이 가장 좋다. 만약 임의의 함수를 허용해야 하지만 호출할 생각이 없다면 () => void 타입이 일반적으로 더 안전하다.
'TIL' 카테고리의 다른 글
[TIL] 23.01.26 패키지 잠금 파일(package-lock.json, yarn.lock) (0) | 2023.01.27 |
---|---|
[TIL] 23.01.17 프리온보딩 챌린지 3회차 (0) | 2023.01.18 |
[TIL] 23.01.11 타입스크립트 핸드북 - More on Functions(1) (0) | 2023.01.12 |
[TIL] 23.01.10 프리온보딩 챌린지를 시작하며 (0) | 2023.01.11 |
[TIL] 22.12.30 타입스크립트 핸드북 - Everyday Types (0) | 2022.12.31 |