타입스크립트 핸드북 14장 - 인덱스 타입
Contents
동적인 프로퍼티 이름을 사용하는 코드를 컴파일러가 알게 하고 싶은 경우
function pluck<T, K extends keyof T>(o: T, propertyNames: K[]): T[K][] {
return propertyNames.map(n => o[n]);
}
interface Car {
manufacturer: string;
model: string;
year: number;
}
let taxi: Car = {
manufacturer: 'Toyota',
model: 'Camry',
year: 2014
};
// Manufacturer과 model은 둘 다 문자열 타입입니다,
// 그래서 둘 다 타이핑된 문자열 배열로 끌어낼 수 있습니다.
let makeAndModel: string[] = pluck(taxi, ['manufacturer', 'model']);
// 만약 model과 year를 끌어내려고 하면,
// 유니언 타입의 배열: (string | number)[] 을 얻게됩니다.
let modelYear = pluck(taxi, ['model', 'year'])
pluck함수는 인자로 입력 받은 프로퍼티의 이름 기반으로 (propertyName) 객체의 프로퍼티를 추출해낸다.keyof T는 인덱스 타입 쿼리 연산자로 사용되었다T는any타입이다.keyof T는T의 공개된 프로퍼티 이름들의 유니언이다.keyof를 사용하였을 때, 컴파일러가 인자로 전달되는 올바른 프로퍼티 집합에 대해 검사한다는 장점
T[K]는 인덱스 접근 연산자로 사용되었다.- 제네릭 컨텍스트에서 사용했기 때문에 조금 난해할지도
person['name']은Person['name']타입을 가진다.
인덱스 타입과 인덱스 시그니쳐
keyof와 T[K]가 인덱스 시그니쳐와 상호작용하여 사용될 수 있다.
인덱스 시그니쳐란
https://radlohead.gitbook.io/typescript-deep-dive/type-system/index-signatures
객체에 들어 있는 어떠한 객체 참조도 문자열을 통해 엑세스 가능하다.
- 자바스크립트의 경우 객체의 인덱스 시그니쳐에 사용된 객체에 대해 암묵적으로
toString()을 호출한다.
let obj = {
toString(){
return 'Hello'
}
}
let foo: any = {};
// 오류: 인덱스 서명은 string, number 여야 함...
foo[obj] = 'World';
// FIX: TypeScript는 명시적으로 호출하게 강제함
foo[obj.toString()] = 'World';
자바스크립트와 달리 타입스크립트에서 사용자가 명시적으로 toString()을 처리하게 하는 이유는 다음과 같다.
- 기본적으로 객체의
toString()구현이 제대로 되어있지 않기 때문이다. - v8 에서는 항상
[object Object]를 반환하는데, 이는foo["object Object"]와 같은 접근을 시도 - 때문에 객체의 인덱스 시그니쳐로
number타입을 지원한다.
interface Dictionary<T> {
[key: string]: T;
}
let keys: keyof Dictionary<number>; // string | number
let value: Dictionary<number>['foo']; // number
interface Dictionary<T> {
[key: number]: T;
}
let keys: keyof Dictionary<number>; // 숫자
let value: Dictionary<number>['foo']; // 오류, 프로퍼티 'foo'는 타입 'Dictionary<number>'에 존재하지 않습니다.
let value: Dictionary<number>[42]; // 숫자