인덱스 시그니처란?
타입스크립트에서 인덱스의 타입을 지정할 때 사용하는 두 개념이다.
두 개념이 밀접한 관련이 있는 개념이지만 엄밀히 말하면 다르긴 하다.
하지만 자주 인덱스 시그니처를 인덱스 타입이라고 부르는 사람도 발생하는 등 조금은 용어 사용이 엄격하지 못한 채로 사용된다.
일단 인덱스 시그니처 먼저 이번 시간에 다뤄보겠다.
- 인덱스 시그니처는 인덱스 타입 정의의 일부로서, 객체가 어떤 형태의 키와 값을 가질 수 있는지를 정의한다.
쉽게 말하자면, 인덱스 시그니처는 '객체 형태의 타입'에서 '속성의 키'를 정의할 때 사용되는 문법이다.
인덱스 시그니처 (Index Signature)
인덱스 시그니처는 특정 객체가 다양한 키를 가질 수 있고, 각 키의 값이 어떤 타입을 가져야 하는지 명시한다.
기본적으로 인덱스 시그니처는 타입을 좀 더 동적으로 처리할 수 있게 해준다. 예시를 보자.
// 인덱스 시그니처 예시
interface StringDictionary {
[key: string]: string; // 인덱스 시그니처
}
const FruitDict = {
A: "Apple",
B: "Banana",
C: "Coconut"
}
// 일부만 인덱스 시그니처로 만들 수 있다.
interface MixedDictionary {
[key: string]: number | string; // 인덱스 시그니처
fixedProperty: boolean; // 명시적으로 정의된 속성
}
let obj: MixedDictionary = {
fixedProperty: true, // 명시적 속성
dynamicProperty1: 42, // 인덱스 시그니처에 따른 속성
dynamicProperty2: "value" // 인덱스 시그니처에 따른 속성
};
'[key: keyValue] : value' 형태로 사용한다. 키값에 대괄호를 넣어 동적으로 여러 값을 허용하는게 ES6의 ComputedPropertyName과 유사한 면이 있다. (관련 내용: https://bumang.tistory.com/160)
인덱스 시그니처가 실제 유용한 경우
사실 이 인덱스 시그니처를 알게 되고 사용하게 된 계기는 아래와 같다.
interface Objs {
id: string;
}
const idObject: Objs = {
id: "string"
}
const constant = {
idKey: "id"
}
// 결과는?
console.log(idObject[constant["idKey"]])
위는 constant용 객체에 상수값을 적어놓고 이를 활용하는 경우이다.
constant["idKey"]가 "id"를 반환하고,
idObject["id"]가 "string"을 반환하니
"string"이 나올것이라 예상했나?
결과적으론 '"id"에 string을 지정할 수 없다는 에러'가 뜬다.. 왜냐하면
interface Objs에 id라는 매우 구체적인 키값을 사용했기 때문이고,
constant["idKey"]가 언제나 "id"라는 보장이 없기 때문이다.
(이렇게 구체적으로 타입지정을 해둘수록, 개발 시 겪게 되는 에러는 많아진다ㅠ)
이럴 경우 어떻게 해결하면 되나? 해결 방법은 방금 배운 인덱스 시그니처를 사용하거나, 타입 단언을 사용하면 된다.
// 선언 시 인덱스 시그니처 사용
// 일단 string은 모두 허용하기 때문에 조금 더 헐거워진 타입체크를 하게된다.
interface Objs {
id: string;
[key: string]: string; // 인덱스 시그니처 추가
}
// 타입 단언을 사용할 시
console.log(idObject[constant["idKey"] as keyof Objs]);