type Rocket = {[property: string]: string};
const rocket: Rocket = {
name: 'Falcon 9',
variant: 'v1.0',
thrust: '4,940 kN',
}; // OK
타입스크립트에서는 인덱스 시그니처
를 이용하여 객체의 타입을 매핑할 수 있습니다.
각각의 키와 타입을 지정하지 않고 한번에 키와 밸류의 타입을 지정할 수 있습니다.
하지만 인덱스 시그니처는 포괄적인 타입선언이기 때문에 모든 키값을 허용하고,
타입스크립트의 언어서비스가 동작하지 않는 등의 특징을 가지고 있습니다.
인덱스 시그니처
{[property: string]: string};
키의 이름: 키의 위치만 표시하는 용도, 타입 체커에서는 사용하지 않습니다.
키의 타입: string이나 number 또는 symbol의 조합이어야 하지만, 보통은 string을 사용합니다.
값의 타입: 어떤 것이든 될 수 있습니다.인덱스 시그니처의 단점
- 잘못된 키를 포함해 모든 키를 허용한다.
- 특정 키가 필요하지 않게 된다.
- 키마다 다른 타입을 가질 수 없게 된다.
- 타입스크립트의 언어 서비스가 동작하지 못하게 된다.
인덱스 시그니처 사용 시기
인덱스 시그니처는 동적 데이터를 표현할 때 사용합니다.
헤더 행과 열에 이름이 있지만, 열 이름이 무엇인지 알 수 없을 때
인덱스 시그니처를 사용하여 타입을 적용할 수 있습니다.
interface Row1 { [column: string]: number } // 너무 광범위함
type Row3 =
| { a: number; }
| { a: number; b: number; }
| { a: number; b: number; c: number; }
| { a: number; b: number; c: number; d: number }; // 가장 정확하지만, 간결하지 않음.
interface Row2 { a: number; b?: number; c?: number; d?: number } // 최선
타입은 가능한 명확하게 지정하는 것이 좋기 때문에
만약 a, b, c, d와 같은 제한적인 키만 제공되지만,
a, b, c, d의 키값이 올 수도 있고 안올 수도 있는 데이터라고 하더라도
인덱스 시그니처를 사용하는 것보다 선택적 필드 또는 유니온 타입을 사용합니다.
인덱스 시그니처를 사용하게 된다면, 키가 string이고, 값이 number라면 어떤 값이든 허용하게 될 것입니다.
type Vec3D = {[k in 'x' | 'y' | 'z']: number};
type ABC = {[k in 'a' | 'b' | 'c']: k extends 'b' ? string : number};
// Type ABC = {
// a: number;
// b: string;
// c: number;
// }
// k가 'b'이면 string타입을, 아닐때는 number타입으로
Vec3D타입과 비교해보면, b는 string 타입을 가지게 된 것을 알 수 있습니다.
솔직히 왜 이런 방법을 사용해서 b만 string으로 사용학게 조건을 거는 방식을 사용했는지는 모르겠습니다.
다만, 저렇게 특정 키의 타입을 지정해서 별도로 지정할 수 있다는 방법 정도만 기억해 두려고 합니다.
인덱스 시그니처는 키값을 정확히 특정할 수 없는 제한된 조건에서 부분적이지만 타입을 지정할 수 있는 방법으로 사용할 수 있을 것 같습니다.