Call Signatures
함수의 타입을 call signatures형태로 작성하여 줄 수 있습니다. 아래의 예시를 보시죠.
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;
이렇게 작성해 주게 되면, add함수 안의 argument타입을 명시해 줄 필요가 없게 됩니다. 아래와 같이 add함수에 반환값을 주게 되면 안된다는 것입니다.
type Add = (a: number, b: number) => number;
// error
const add: Add = (a, b) => {
a + b;
};
Overloading
overloading은 동일한 이름에 매개 변수와 매개 변수 타입 또는 리턴 타입이 다른 여러 버전의 함수를 만드는 것을 말합니다. Typescript에서는 오버로드 signatures을 작성하여 다양한 방식으로 호출할 수 있는 함수를 지정할 수 있습니다. 예시는 다음과 같습니다.
type Add = {
(a: number, b: number): number;
(a: number, b: string): number;
};
const add: Add = (a, b) => {
if (typeof b === "string") return a;
return a + b;
};
add함수의 b의 type은 number | string이 됩니다. 따라서 위와 같이 처리해 주어야 합니다. 이는 Next.js의 router예시에서도 활용 예시를 찾아볼 수 있습니다.
Router.push({
path: "/home",
state: 1,
});
Router.push("/home");
다음과 같이 push의 인수로 객체를 줄수도, 문자열을 줄수도 있습니다. 이 때 함수의 오버로딩을 쓰게 되는 겁니다. 이러한 Router의 오버로딩을 구현하려면 아래와 같이 작성해 주면 됩니다.
type Config = {
path: string;
state: object;
};
type Push = {
(path: string): void;
(config: Config): void;
};
const push: Push = (config) => {
if (typeof config === "string") {
console.log(config);
} else {
console.log(config.path);
}
};
이런 오버로딩은 패키지나, 라이브러리를 디자인할 때 많은 사람들이 사용합니다. 이 외에도 만약 인자가 다른 함수의 signauture 여러개가 들어오게 된다면 아래와 같이 처리해 주면 됩니다.
type Add = {
(a: number, b: number, c: number): number;
(a: number, b: number): number;
};
const add: Add = (a, b, c?: number) => {
if (c) return a + b + c;
return a + b;
};
// works
add(1, 2);
add(1, 2, 3);
이 코드의 의미는 c는 추가적인 option이다 라는 점입니다. 이것도 많이 쓰는 패턴입니다.
Polymorphism
이제까지 우리는 다형성을 구현해 왔습니다. 다형성이란 여러 형태를 뜻하는 거십니다. 우선 예제부터 보시죠
type SuperPrint = {
(arr: number[]): void;
(arr: boolean[]): void;
(arr: string[]): void;
};
const superPrint: SuperPrint = (arr) => {
arr.forEach((i) => console.log(i));
};
superPrint([1, 2, 3, 4]);
superPrint([true, false, true]);
superPrint(["a", "b", "c"]);
이러한 코드가 있는데 너무 반복적이고 비 효율적입니다. 이와 같이 concrete type인 (number, boolean, string)을 각각 사용하기 보단, generic을 사용해 주면 좋을 것 같습니다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void;
};
const superPrint: SuperPrint = (arr) => {
arr.forEach((i) => console.log(i));
};
superPrint([1, 2, 3, 4]);
superPrint([true, false, true]);
superPrint(["a", "b", "c"]);
superPrint([1, 2, true, false]);
이렇게 하게 되면 <TypePlaceholder>에 <number>, <boolean>, <string>, <number | boolean>이 각각 들어가게 되면서 TypeSciprt가 추론을 할 수 있게 됩니다. 또한 반환값도 Generic으로 간편하게 지정해 줄 수 있습니다.
type SuperPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): TypePlaceholder;
};
const superPrint: SuperPrint = (arr) => arr[0];
const a = superPrint([1, 2, 3, 4]);
const b = superPrint([true, false, true]);
const c = superPrint(["a", "b", "c"]);
const d = superPrint([1, 2, true, false, "a"]);
SuperPrint의 형태가 Generic을 통해 다향해 졌는데, 이를 다형성이라고 합니다. 간단하게 아래와 같이 표현하는 것이 일반적이기도 합니다.
type SuperPrint = {
<T>(arr: T[]): T;
};
const superPrint: SuperPrint = (arr) => arr[0];
const a = superPrint([1, 2, 3, 4]);
const b = superPrint([true, false, true]);
const c = superPrint(["a", "b", "c"]);
const d = superPrint([1, 2, true, false, "a"]);
Conclusions
우리는 위의 코드를 다음과 같이 작성해 줄 수 있기도 합니다.
function superPrint<V>(a: V[]): V {
return a[0];
}
const a = superPrint([1, 2, 3, 4]);
const b = superPrint([true, false, true]);
const c = superPrint(["a", "b", "c"]);
const d = superPrint([1, 2, true, false, "a"]);
const a = superPrint<number>([1, 2, 3, 4]);
이렇게 타입을 명시해 줄 수도 있는데, 그냥 TypeScript가 유추할 수 있게 해주는게 가장 좋습니다.
또한 제네릭을 사용하면서 확장가능하게 작성해 주는 것이 좋습니다. 다음과 같이 말이죠
type Player<E> = {
name: string;
extraInfo: E;
};
type HyunseoExtra = {
favFood: string;
};
type HyunseoPlayer = Player<HyunseoExtra>;
const user1: HyunseoPlayer = {
name: "hyunseo1",
extraInfo: {
favFood: "kimchi",
},
};
Player안에 HyunseoPlayer 안에 HyunseoExtra이렇게 말이죠, 일종의 type끼리 상속을 한다고 보시면 적절할 것 같습니다. 큰 타입 안에 어떠한 변화할 수 있는 타입이 있다면, 그 안에 또 타입을 넣고... 이런 식으로 작성 하는 겁니다.
JS의 Array도 Generic을 사용한 것입니다. 아래의 예시를 봅시다.
type A = Array<number>;
let a: A = [1, 2, 3, 4, 5];
function printAllNumbers(arr: Array<number>) {}
다음과 같이 Array도 Generic이므로 <number>로 타입을 지정해 줄 수 있고 number[]을 Array<number>타입으로 다르게 써 줄 수도 있습니다.
'Web > TypeScript' 카테고리의 다른 글
Typescript Practice - Block Chain Project -implements (0) | 2022.05.08 |
---|---|
Typescript Practice - Block Chain Project - Basic (0) | 2022.05.07 |
TypeScript Practice - Class, Interface (0) | 2022.05.07 |
Typescript Practice - OverView (0) | 2022.05.07 |