제네릭
타입을 나중에 정하는 함수와 형태를 만듭니다.
타입을 나중에 정합니다
배열의 첫 값을 꺼내는 함수를 만든다고 해 봅시다. 숫자 배열에도, 글자 배열에도 쓰고 싶습니다. 타입을 무엇으로 적을까요.
function first(list: unknown[]): unknown { return list[0]; } const b = first(["홍길동", "김철수"]); console.log(b.toUpperCase());
unknown 으로 적으면 어떤 배열이든 받지만,
꺼낸 값이 무엇인지 잃어버립니다. 부르는 쪽에서 다시 확인해야 합니다.
제네릭은 "넣은 것과 같은 것이 나온다" 를 적는 방법입니다.
function first<T>(list: T[]): T | undefined { … } // ↑ 지금은 모르지만, 부를 때 정해지는 타입
타입이 따라옵니다
function first<T>(list: T[]): T | undefined { return list[0]; } const a = first([1, 2, 3]); const b = first(["홍길동", "김철수"]); console.log(a, b); console.log(b?.toUpperCase());
<number> 처럼 적어 주지 않았는데도
a 는 숫자, b 는 글자입니다.
넘긴 배열을 보고 T 가 무엇인지 정해집니다.
b?. 를 붙인 것은 반환 타입이
string | undefined 이기 때문입니다 — 빈 배열이면
꺼낼 것이 없습니다(1.6).
여러 개를 둘 수 있습니다
function pair<K, V>(key: K, value: V): [K, V] { return [key, value]; } const a = pair("age", 27); const b = pair(1, true); console.log(a[0], a[1]); console.log(b[0], b[1]);
이름은 아무렇게나 지어도 됩니다. T ·
K · V 는 그저 관례입니다
(type · key · value).
이미 사용해 왔습니다. Array<number>(1.3)
와 Promise<string> 이 제네릭입니다 — 무엇이
담기는지를 꺾쇠 안에 적는 것입니다.
아무것이나 받지 않게 하기
T 로 두면 정말 무엇이든 들어옵니다. 함수 안에서
length 를 사용한다면 길이가 있는 것만 받아야
합니다.
function longest<T extends { length: number }>(a: T, b: T): T { return a.length >= b.length ? a : b; } console.log(longest("홍길동", "김철수입니다")); console.log(longest([1, 2], [1, 2, 3]).length);
extends 가 여기서는 물려받는다는 뜻이 아니라
"적어도 이것은 갖춘 것" 이라는 조건입니다. 글자와 배열은 둘 다
length 를 가지고 있어 지나갑니다.
function longest<T extends { length: number }>(a: T, b: T): T { return a.length >= b.length ? a : b; } console.log(longest("홍길동", "김철수입니다")); console.log(longest([1, 2], [1, 2, 3]).length); console.log(longest(1, 2));
숫자에는 length 가 없어 막혔습니다. 조건을 적어
두지 않았다면 함수 안쪽의 a.length 가 먼저
막혔을 것입니다.
처음에 자주 걸리는 것
first<string>(list) 처럼 직접 적을 수도
있지만 대개 필요 없습니다. 넘긴 값을 보고 정해집니다. 정해지지 않을
때만 적습니다.
T 는 들어오는 자리와 나가는 자리를 잇는
끈입니다. 매개 변수에 한 번도 나오지 않으면 정해질 방법이 없어
의미가 없습니다.
제네릭이 어려워 any 로 적으면 그 자리의 검사가
꺼집니다(1.5). 제네릭은 검사를 유지한 채 여러 타입에 쓰는 방법입니다.
직접 해보기
배열의 마지막 값을 꺼내는 제네릭 함수를 만드세요.
function last<T>(list: T[]): T | undefined {
return list[list.length - 1];
}
console.log(last([1, 2, 3]));
console.log(last(["홍길동", "김철수"]));
console.log(last([]));
name 을 가진 것만 받아 그 이름을 돌려주는 제네릭
함수를 만드세요.
function nameOf<T extends { name: string }>(item: T): string {
return item.name;
}
console.log(nameOf({ name: "홍길동", age: 27 }));
console.log(nameOf({ name: "연필", price: 1000 }));이 단원의 정리
- 제네릭은 "넣은 것과 같은 것이 나온다" 를 적는 방법입니다.
- 꺾쇠 안의 이름은 부를 때 정해집니다.
- 대개 직접 적지 않아도 넘긴 값을 보고 정해집니다.
extends로 "적어도 이것은 갖춘 것" 이라는 조건을 답니다.Array<T>·Promise<T>도 제네릭입니다.