제네릭 (extends, infer)
2022. 12. 26. 16:44ㆍ프로그래밍/TypeScript
제네릭 (Generic)
재사용을 목적으로 함수나 클래스의
사용 시점에 타입을 선언하는 방법
// T는 타입 변수
// 사용자가 제공한 타입으로 변환될 식별자
// 다른 이름으로 변경 가능
function toArray<T>(a: T, b: T): T[] {
return [a, b];
}
toArray<number>(1, 2);
toArray<string>('1', '2');
toArray<string | number>(1, '2');
toArray<number>(1, '2'); // Error. number 타입만 인수로 전달 가능
// 타입 추론 활용
// 사용 시점에 인수 타입을 제공하지 않을 수도 있음
toArray(1, 2);
toArray('1', '2');
toArray(1, '2') // Error. 두 타입은 같아야 함
제약 조건 (Constraints)
인터페이스/타입 별칭을 사용하는 제네릭 작성 가능
extends 키워드를 사용해 제약 조건 추가 가능
T extends U
// 타입변수 T는 string, number 타입만 허용함
// extends 키워드 사용
interface MyType<T extends string | number> {
name: string,
value: T
}
const dataA: MyType<string> = {
name: 'Data A',
value: 'Hello world'
}
const dataB: MyType<number> = {
name: 'Data B',
value: 1234
}
const dataC: MyType<boolean> = { // Error. string, number 타입만 가능
name: 'Data C',
value: true
}
const dataD: MyType<number[]> = { // Error. string, number 타입만 가능
name: 'Data D',
value: [1, 2, 3, 4]
}
type U = string | number | boolean;
// type 식별자 = 타입 구현
type MyType<T extends U> = string | T;
const t: MyType<number> = '1';
// interface 식별자 { 타입 구현 }
interface User<T extends U> {
name: string,
age: T
}
const u: User<number> = {
name: 'Heropy',
age: 85
};
조건부 타입 (Conditional Types)
타입 구현 영역에서 사용하는 extends 키워드
삼항 연산자 사용
T extends U ? X : Y
type U = string | number | boolean;
// type 식별자 = 타입 구현
type MyType<T> = T extends U ? string : never;
// interface 식별자 { 타입 구현 }
interface IUser<T> {
name: string,
age: T extends U ? number : never;
}
// T는 boolean 타입으로 제한
interface IUser<T extends boolean> {
name: string,
// T의 타입이 true인 경우 string 반환
// 아닌 경우 number 반환
age: T extends true ? string : number,
isString: T
}
const str: IUser<true> = {
name: 'Neo',
age: '12', // String
isString: true
}
const num: IUser<false> = {
name: 'Lewis',
age: 12, // Number
isString: false
}
infer
타입 변수의 타입 추론 여부 확인 가능
infer 키워드 사용
T extends infer U ? X : Y
U 가 추론 가능한 타입이면 true, 아니면 false
// 타입 변수 R은 MyType<number>에서 받은 타입 number가 됨
// number타입은 타입 추론이 가능하므로 R을 반환함
type MyType<T> = T extends infer R ? R : null;
// MyType<number>는 number를 반환함
// 변수 a는 123을 할당할 수 있음
const a: MyType<number> = 123;
// 타입 변수 R은 string
// string타입은 타입 추론이 가능하므로 R을 반환함
// ReturnType는 함수의 반환 값이 어떤 타입인지 반환함
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
// (num: number) => string
function fn(num: number) {
return num.toString();
}
// typeof fn은 string
// ReturnType<typeof fn>은 string 할당 가능
const a: ReturnType<typeof fn> = 'Hello';
- 제약 조건 extends가 아닌 조건부 타입 extends 절에서만 사용 가능
- 같은 타입 변수를 여러 위치에서 사용 가능
- 여러 호출 시그니처(함수 오버로드)의 경우 마지막 시그니처에서 추론