728x90
반응형
SMALL
호출 시그니처(Call Signatures)
프로퍼티로 호출 가능한 것을 설명하려면 객체 타입에 Call Signature을 작성할 수 있다.
Call Signatures는 다음과 같이 함수의 매개 변수(parameter)와 반환 타입을 지정하여 이 함수가 어떻게 호출되는지 설명해주는 부분을 말한다.
const add = (a : number, b : number) => a + b;
// 위의 코드에서 a와 b의 타입 지정을 하고 싶지 않다면 call signature 타입을 만들어 사용
// call signature 타입 사용 후
type Add = (a : number, b : number) => number; // call signature
const add : Add = (a, b) => a + b;
오버로딩(Overloading)
우리가 실제로 구현하게 되면 패키지나 라이브러리들을 자주 사용하게 될 텐데 이때 패키지나 라이브러리들은 오버로딩을 엄청 많이 사용한다.
오버로딩은 함수가 서로 다른 형태의 여러 개 call signatures를 가지고 있을 때 발생시킨다.
// 매개변수의 데이터 타입이 다른 경우 예외 처리
type Add = {
(a: number, b: number) : number,
(a: number, b: string) : number,
}
const add : Add = (a, b) => a + b; // 에러, 이때 b는 string | number 타입인데 string + number가 불가능하기 때문
const add: Add = (a, b) => { // 타입에 따라 예외처리를 해줘야 함
if (typeof b === "string") return a;
return a + b;
}
// 매개변수의 수가 다른 경우 예외 처리, 이런 경우는 잘 없지만 외부 라이브러리에서 활용될 수 있다.
type Add2 = {
(a: number, b: number) : number,
(a: number, b: number, c: number) : number
}
const add2: Add2 = (a, b, c?: number) => {
if (c) return a + b + c;
return a + b;
}
// 일상생활에서 개발할 때 볼 수 있는 오버로딩 예시
router.push("/home");
router.push({
path: "/home",
state: 1
});
// Next.js의 라우터 push가 대충 두 가지 방법으로 페이지를 이동한다고 할 때
// 패키지나 라이브러리는 아래와 같이 두 가지 경우의 오버로딩으로 디자인되어 있을 것이다.
type Config = {
path: string,
state: number
}
type Push = {
(config: Config): void,
(config: string): void
}
const push: Push = (config) => {
if (typeof config === "string") console.log(config);
else console.log(config.path);
}
다형성(Polymorphism)
poly는 many, several, much와 같은 뜻이고 morphism은 form, structure와 같은 뜻이다. 즉 다형성이란 여러가지 다른 모양(형태)를 뜻한다.
인자들과 반환값에 대하여 형태(타입)에 따라 그에 상응하는 형태(타입)를 갖을 수 있다.
type SuperPrint = {
(arr : number[]) : void,
(arr : string[]) : void,
(arr : boolean[]) : void,
}
const superPrint : SuperPrint = (arr) => {
arr.forEach(i => console.log(i));
}
const a = superPrint([1, 2, 3]);
const b = superPrint([true, false, true]);
const c = superPrint(["a", "b"]);
const d = superPrint([1, 2, "a", "b"]); // 에러, number | string 타입이 명시되어 있지 않기 때문
// 때문에 위와 같이 일일이 주는 대신 generic(타입의 placeholder와 같은 것)을 사용
제네릭(Generic) 타입
- 사용하는 이유
- 우리가 call signature을 작성할 때 여기 들어올 확실한 타입을 모를 때 generic을 사용한다.
- C#이나 Java와 같은 언어에서 재사용 가능한 컴포넌트를 만들기 위해 사용하는 기법이다.
- 단일 타입이 아닌 다양한 타입에서 작동할 수 있는 컴포넌트를 생성할 수 있다.
- 구체적인 타입을 지정하지 않고 다양한 인수와 리턴 값에 대한 타입을 처리할 수 있다.
- 타입스크립트에서 제네릭을 통해 인터페이스, 함수 등의 재사용성을 높일 수 있다.
- any와의 차이점
- 해당 타입에 대한 정보를 잃지 않는다.
- any는 any로서 밖에 알 수 없지만 generics는 타입 정보를 알 수 있다.
// type SuperPrint = <T>(arr : T[]) => T // generic 사용, Typescript가 타입을 유추
// const superPrint : SuperPrint = (arr) => {
// return arr[0];
// }
// 위의 방법보다 더 자주 쓰일 generic 사용한 코드
function superPrint<T>(arr : T[]){
return arr[0];
}
const a = superPrint([1, 2, 3]);
const b = superPrint([true, false, true]);
const c = superPrint(["a", "b"]);
const d = superPrint([1, 2, "a", "b", true]);
a.toUpperCase();
// any를 사용하면 위와 같은 경우에도 에러가 발생하지 않는다.
// generic을 사용하면 에러가 발생해 보호받을 수 있다.
type Player<E> = {
name : string,
info: E
}
type Age = {
age : number
}
type firstPlayer = Player<Age>
const person : firstPlayer = {
name: "hi",
info: { age: 12 }
}
const person2 : Player<null> = {
name: "hi2",
info: null
}
아래는 실제로 있는 generic 사용 예시이다.
type arr = Array<number>
let a : arr = [1, 2, 3, 4];
728x90
반응형
LIST
'프론트엔드 > TypeScript' 카테고리의 다른 글
[TypeScript] type와 interface의 차이, class (0) | 2024.01.21 |
---|---|
[TypeScript] 타입스크립트의 타입 별칭(type alias) (0) | 2024.01.19 |
[TypeScript] 타입스크립트는 왜 좋은 프로그래밍 언어일까? (0) | 2024.01.18 |