객체가 중간에 변경될 수 있고, 새로운 프로퍼티와 메서드를 사용해야 할 수도 있다.
특히 외부 파일에서 csv파일 같은 것을 불러와 가져오는 경우, 열의 이름이 무엇인지 미리 알 수 없다. 이럴 경우에는 인덱스 시그니처를 적극적으로 이용해 볼 수 있다.
인덱스 시그니처는 동적 데이터를 작성할 때 매우 유용하다.
function parseCSV(input: string): {[columnName: string]: string}[] }
const lines = input.split('\n');
const [header, ...rows] = lines;
const headerColumns = header.split(',');
return rows.map(rowStr => {
const row: {[columnName: string]: string } = {};
rowStr.split(',').forEach((cell, i) => {
row[headerColumns[i]] = cell;
});
return row;
});
}
[columnName: string]
이 일치하기만 한다면, 인덱스 시그니처에 따라 객체가 동적으로 생성될 수 있다.
하지만 타입 체크가 실행될 때 인덱스 시그니처의 단점은 드러나게 된다.
의도하지 않은 프로퍼티를 사용하게 되어도 타입 체커가 오류를 잡지 못한다.
type Rocket = {[property: string]: string};
const rocket: Rocket = {
name: 'Falcon 9',
variant: 'v1.0',
thrust: '4,940KN'
});
여기서 name
대신 Name
을 사용해도 문제가 발생하지 않는다.
{}
도 유효한 Rocket
의 값이 될 수 있다.
꼭 값이 string
이 아니라, number
가 될 수도 있는데 이를 수정하거나 지정할 수 없다.
선언해 둔 열이 런타임에 실제로 일치한다는 보장을 할 수 없다. 그래서 undefined
가 들어오는 것도 허락을 해 준 뒤에 이후 undefined
를 제하는 오류 체크를 해야 한다.
function safeParseCSV(
input: string
): {[columnName: string]: string | undefined}[] {
return parseCSV(input);
};
정리하자면 인덱스 시그니처는 부정확하기도 하고 타입스크립트의 장점을 최대한 활용하기 어렵습니다. 그렇기 때문에 타입이 정확하게 정해져 있다면 인터페이스를 사용하는 것을 추천합니다.
어떤 타입에 가능한 필드가 제한되어 있을 경우, 인덱스 시그니처로 모델링하는 것은 좋지 않다. A, B, C, D라는 키가 정해져 있지만, 그들이 얼마나 많이 있는지 모르겠다면 아래와 같이 모델링 할 수 있다.
interface Row1 {[column: string]: number } //너무 광범위
interface Row2 { a: number; b?: number; c?: number; d?: number } //최선
type Row3 =
| { a: number; }
| { a: number; b: number; }
| { a: number; b: number; c: number; }
| { a: number; b: number; c: number; d: number };
키 타입에 유연성을 제공하는 제너릭 타입이다.
type Vec3D = Record<'x' | 'y' | 'z', number>;
매핑된 타입은 키마다 별도의 타입을 사용하게 해 준다.
type Vec3D = {[k in 'x' | 'y' | 'z' ]: number};
type ABC = {[k in 'a' | 'b' | 'c' ]: k extends 'b' ? string : number};
이렇게 작성하면 b
에 대해서는 string
과 number
타입을 모두 사용할 수 있다.
타입스크립트는 자바스크립트의 객체 모델을 이용하여 모델링하였기 때문에, 자바스크립트의 객체 모델링과 동작에 대하여 잘 알고 있어야 한다.
먼저, 배열은 객체이다.
객체의 키는 보통 문자열로 표현된다. 그렇기 때문에 아래와 같은 현상이 일어난다.
toString
메서드가 호출되어 객체가 문자열로 변환된다.Object.keys
을 이용하면 문자열 키가 출력된다.타입스크립트에서는 이러한 자바스크립트의 혼란을 줄이기 위해서 숫자 키를 허용하고, 문자열 키와는 다른 것으로 인식합니다.
사실, 타입스크립트에서 문자열 키를 따로 체크하는 과정이 있는 것은 아니고, 타입 체크 시점에 인덱스에 대한 타입을 확인하는 과정에서 오류를 잡아낼 수 있는 것이다.
interface Array<T> {
//...
[n: number]: T;
}
const xs = [1, 2, 3];
const x0 = xs[0];
const x1 = xs['1']; //인덱스 식이 number형식이 아닙니다.
타입 정보는 런타임시 제거된다. 그래서 코드는 실제로 동작하지 않는 가상코드이지만, 타입 체커에서 오류를 잡아낼 수 있다는 것에 의의가 있다.
만약, 인덱스의 타입이 중요하다면, number
타입을 제공해주는 forEach를 이용하자!
그리고 루프 중간에 멈춰야 한다면 for
루프를 사용할 수 있는데, 느리므로 자주 사용하지는 말자.