제네릭은 언어에서 재사용성이 높은 컴포넌트를 만들 때 자주 활용되는 특징이다
제네릭은 타입을 마치 함수의 파라미터처럼 사용하는 것을 의미한다.
특히, 한가지 타입보다 여러가지 타입에서 동작하는 컴포넌트를 생성하는 데 사용된다.
아래 코드는 인자를 하나 넘겨 받아 반환해주는 함수입니다.
여기서 이 함수의 인자와 반환 값은 모두 string
으로 지정되어 있습니다.
function getText<string>(text: string): string {
return text;
}
만약 여러 가지 타입을 허용하고 싶다면 아래와 같이 any
를 사용할 수 있습니다.
function logText(text: any): any {
return text;
}
이렇게 타입을 바꾼다고 해서 함수의 동작에 문제가 생기진 않습니다.
다만, 함수의 인자로 어떤 타입이 들어갔고 어떤 값이 반환되는지는 알 수가 없습니다.
왜냐하면 **any
라는 타입은 타입 검사를 하지 않기 때문**입니다.
이러한 문제점을 해결할 수 있는 것이 제네릭입니다. 아래 코드를 보겠습니다.
function logText<T>(text: T): T {
return text;
}
먼저 함수의 이름 바로 뒤에 <T>
라는 코드를 추가했습니다.
그리고 함수의 인자와 반환 값에 모두 T
라는 타입을 추가합니다.
이렇게 되면 함수를 호출할 때 넘긴 타입에 대해 타입스크립트가 추정할 수 있게 됩니다.
따라서, 함수의 입력 값에 대한 타입과 출력 값에 대한 타입이 동일한지 검증할 수 있게 됩니다.
그리고 이렇게 선언한 함수는 아래와 같이 2가지 방법으로 호출할 수 있습니다.
// #1
const text = logText<string>("Hello Generic");
// #2
const text = logText("Hello Generic");
보통 두 번째 방법이 코드도 더 짧고 가독성이 좋기 때문에 흔하게 사용됩니다.
그렇지만 만약 복잡한 코드에서 두 번째 코드로 타입 추정이 되지 않는다면 첫 번째 방법을 사용하면 됩니다.
// 1.문자/숫자/불린 기본타입
const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => {
return [arg3, arg2, arg1];
};
const result1 = getPrimitive("철수", 123, true);
//
//
// 2. any 타입(자바스크립트와 동일)
const getAny = (arg1: any, arg2: any, arg3: any): [any, any, any] => {
console.log(arg1 + 100); // any 는 아무거나 다 됨!
return [arg3, arg2, arg1];
};
const result2 = getAny("철수", 123, true);
//
//
// 3. unknown 타입
const getUnknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => {
if (typeof arg1 === "number") console.log(arg1 + 100); // any 보다는 안전
return [arg3, arg2, arg1];
};
const result3 = getUnknown("철수", 123, true);
//
//
// 4-1. generic 타입(들어오는 타입에 따라서 MyType이 설정된다.)
function getGeneric<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
return [arg3, arg2, arg1];
}
const result4 = getGeneric("철수", 123, true);
//
//
// 4-2. generic 타입(들어오는 타입을 고정시키기 위해서는 아규먼트에 <> 추가 해줘야한다.)
function getGeneric2<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
return [arg3, arg2, arg1];
}
const result5 = getGeneric2<string, number, boolean>("철수", 123, true);
//
//
// 4-3. generic 타입
function forRoot<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
return [arg3, arg2, arg1];
}
const result6 = forRoot("철수", 123, true);
//
//
// 4-4. generic 타입
function getGeneric4<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
return [arg3, arg2, arg1];
}
const result7 = getGeneric4<string, number, boolean>("철수", 123, true);
//
//
// 4-5. generic 타입 - 4 (화살표 함수 사용)
const getGeneric5 = <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => {
return [arg3, arg2, arg1];
};
const result8 = getGeneric5<string, number, boolean>("철수", 123, true);
Generic Type은 우리가 함수를 만들어서 제공해 줄 때 안전하고 확장성 높은 코드 사용을 위해 많이 사용한다.
중요
꺽새를 항상 사용하여 임의의 함수를 지정해주기.
타입 명시를 해주어 에러 발생을 막아주기.
Utility Type은 기존에 있던 Type들을 변형해서 변형된 타입을 새로 만들어주는 역할을 하며,
코드의 가독성을 높이기 위해(같은 코드를 여러번 작성하지 않기 위해) 많이 사용한다.
interface IProfile {
name: string;
age: number;
school: string;
hobby?: string;
}
//1. Partial 타입 (부분:있어도 되고 없어도 되는)
type aaa = Partial<IProfile>;
//2. Required 타입 (필수사항)
type bbb = Required<IProfile>;
//3. Pick 타입 (원하는 속성만 뽑아서 사용하고 싶을 때)
type ccc = Pick<IProfile, "name" | "age">;
//4. Omit 타입 (원하는 속성 제거하여 사용하고 싶을 때 )
type ddd = Omit<IProfile, "school">;
//5. Record 타입
type eee = "철수" | "영희" | "훈이"; // Union 타입
let child1: eee = "철수"; // 철수,영희,훈이만 됨
let child2: string = "사과"; // 철수 영희 훈이 사과 바나나 다됨
type fff = Record<eee, IProfile>; // Record 타입
//6.객체의 키들로 Union 타입 만들기
type ggg = keyof IProfile; // "name" | "age" | "school" | "hobby"의 Union타입을 만들어줌
let myprofile: ggg = "hobby";
//7. type vs interface 차이 => interface는 선언병합 가능
interface IProfile {
candy: number; // 선언병합으로 추가됨
}
// 8.배운것 응용
let profile: Partial<IProfile> = {
candy: 10,
};
위의 예시만 본다면 이해가 가지만 Union 타입
과 Record 타입
는 이해가 안 갈 수도 있다.
이 두가지에 대해 알아보도록 하자.
Union 타입
:Javascript의 OR 연산자( ||
)와 같이 ‘A’ 이거나 ‘B’이다 라는 의미의 타입으로,
**|**
연산자를 이용하여 타입 또는 값을 여러 개 연결할 때 사용할 수 있다.
유니온타입으로 key값을 저장해주면 다른 변수에 할당에 주면 그 유니온 타입으로 지정된 key값만 사용할 수 있다.
Record 타입
// index.ts
interface IProfile {
name: string;
age: number;
school: string;
hobby?: string;
}
// 5. Record 타입
type eee = "철수" | "영희" | "훈이"; // Union 타입
type fff = Record<eee, IProfile>; // Record 타입
Union Type 속성을 다른 Type으로 매핑
시키고자 할 때 사용한다.
위의 예제를 보면 유니온 타입으로 철수 영희 훈이
의 key 값을 받아오고 Record<>
을 통해
IProfile 를 value 값으로 매핑 된 것
을 확인 할수 있다.