sungyup's.

typescript / Advanced Concepts / 2.2 Generic Types

2.2Generic Types

함수/클래스/타입이 타입을 인자로 받는 Generic Type

TL;DR

추억의 쪽지 시험

Generic Type

Generic Type란, 함수/클래스/타입이 값이 아닌 타입을 인자로 받는 문법이다. 이를 통해 타입스크립트가 보다 정확하게 타입을 추론할 수 있게 되고, 함수/클래스/타입의 재사용성이 좋아진다.

예를 들어, Generic Type이 아닌 아래의 DataStore 타입은 키는 문자열, 값은 문자열 또는 숫자만 받기에 불리언 값을 가진 속성을 추가할 수 없다.

typescript
type DataStore = {
[key: string]: string | number
};
const store: DataStore = {};
store.name = 'sungyup';
store.isDeveloper = true; // 에러! 불리언 값은 type에 없다.

Generic Type은 <>안에 placeholder를 두고, 이후 실제 해당 타입을 사용할 때 타입을 인자로 받아 placeholder 자리에 적용한다. 보통 대문자 T가 많이 쓰인다.

typescript
type DataStore<T> = {
[key: string]: T
};
const store: DataStore<string | boolean> = {};
store.name = 'sungyup';
store.isDeveloper = true; // T가 string 또는 boolean이라 boolean도 가능하다.

타입 뿐 아니라 함수 또는 클래스를 정의할때도 Generic을 활용할 수 있다.

함수의 예

함수에선 특히 입력과 출력의 타입이 서로 연결되어야 하는 경우가 많다. 범용적인 함수를 만든다고 any를 썼다가는 아무거나 받아서 아무거나 반환하는 함수를 만들수도 있다. 이 때, Generice을 활용하면 T라는 타입을 받아 T를 반환하는 함수를 만들 수 있게 된다.

typescript
function merge<T>(a: T, b: T) { return [a, b]; } const ids = merge<number>(1, 2); // <number>은 생략되어도 타입스크립트가 추론할 수 있다.

클래스의 예

typescript
class User<T>{ constructor(public id: T){} }; const user = new User('i1'); user.id;

또, Generic을 쓸 때 인자 여러 개를 동시에 쓸 수도 있다. 이 때 주의해야할 것은 반환 타입도 명시적으로 작성해야할 경우가 많다는 것이다. 예를 들어, 아래의 예시에서 반환 타입을 지정하지 않는다면 타입스크립트는 (T|U)[]로 반환 타입을 추론할 것이다.

typescript
function merge<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
const ids = merge(1, 'sungyup'); // U라는 다른 generic이기 때문에 b 자리엔 다른 타입이 올 수 있다.

Type Constraints

Generic의 핵심은 타입 제한이다. 사실, <T>를 받는다고만 하면 any로 지정하는 것과 비교했을 때 큰 이점이 없어지므로 extends를 활용해 특정 속성은 반드시 포함한 객체 등의 타입을 지정하는 패턴이 자주 쓰인다.

typescript
// 1) 제약: length가 있는 것만 function sized<T extends { length: number }>(x: T) { return x.length; } // 2) 안전한 프로퍼티 접근 function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user = { id: '1', age: 33 }; const age = getProp(user, 'age'); // number // 3) 키 제약(Record 등) function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T,K> { const out = {} as Pick<T, K>; for (const k of keys) out[k] = obj[k]; return out; }

보다 실전적이고 구체적인 예시는 다음 포스팅에서 살펴보자.