노트

인덱스 시그니처

Index Signatures

CS#ts · 연결된 개념 5개

쉽게 말하면

인덱스 시그니처는 사물함이 몇 칸일지 몰라도 '어느 칸이든 신발만 넣는다'는 규칙을 붙여 두는 거예요. 칸 이름을 미리 몰라도 아무 칸에서 꺼낸 물건이 신발이라는 건 알 수 있죠.

비유의 한계 이 규칙은 이름을 붙여 둔 칸에도 똑같이 걸려요. 그리고 빈 칸을 열어도 타입은 신발이라고 말해서, 없는 키를 읽으면 실제로는 undefined가 나올 수 있어요.

인덱스 시그니처(Index Signature)는 속성 이름을 미리 다 알 수는 없지만 값의 모양은 알 때, [key: string]: T처럼 "어떤 키로 꺼내든 값은 T"라고 적는 TypeScript 문법이다. 키를 모르는 사전(dictionary) 모양의 객체를 표현할 때 쓴다.

interface Scores {
  [name: string]: number
}
const scores: Scores = { kim: 90, lee: 85 }
scores['park'] = 70          // 어떤 문자열 키든 된다
  • 키 타입으로는 string, number, symbol, 템플릿 문자열 패턴(`data-${string}`), 그리고 이들로만 이뤄진 유니언을 쓸 수 있다(템플릿 리터럴 타입)
  • number 인덱서와 string 인덱서를 함께 두면, 숫자 인덱서의 값 타입은 문자열 인덱서 값 타입의 하위 타입이어야 한다. 자바스크립트는 숫자 키를 문자열로 바꿔 찾기 때문이다(obj[100]은 obj["100"]과 같다)

이름 붙은 속성도 규칙을 따른다

문자열 인덱스 시그니처는 obj.prop을 obj["prop"]으로도 읽을 수 있다는 뜻이다. 그래서 같은 타입에 적은 이름 붙은 속성도 인덱스 시그니처의 값 타입에 맞아야 한다.

interface Dict {
  [key: string]: number
  length: number             // OK
  name: string               // 에러: string은 number에 맞지 않는다
}
interface Mixed {
  [key: string]: number | string
  length: number             // OK
  name: string               // OK: 값 타입이 유니언이면 함께 둘 수 있다
}

readonly 인덱스 시그니처

앞에 readonly를 붙이면 인덱스로 값을 읽기만 할 수 있고 대입은 에러가 된다.

interface ReadonlyScores {
  readonly [name: string]: number
}
declare const s: ReadonlyScores
s['kim'] = 100               // 에러: 읽기만 허용된다

없는 키를 읽을 때

선언하지 않은 키도 인덱스 시그니처 덕분에 값 타입 그대로(number) 취급된다. 실제로 없는 키를 읽으면 런타임에는 undefined가 나온다. noUncheckedIndexedAccess 옵션을 켜면 선언하지 않은 필드를 읽은 결과에 undefined가 더해져 검사를 강제할 수 있다(tsconfig 핵심 옵션, 타입 좁히기와 타입 가드).

관련 문법

  • 매핑된 타입({ [K in Keys]: T })은 인덱스 시그니처 문법을 바탕으로, 정해진 키 목록을 돌며 타입을 만든다
  • 키와 값 타입으로 객체 타입을 만드는 유틸리티 타입 Record<Keys, Type>도 있다. 사전 모양은 Record<string, number>처럼 적을 수도 있다

출처: TypeScript Handbook - Index Signatures · TSConfig - noUncheckedIndexedAccess · Mapped Types · Utility Types - Record

연결된 개념

이 노트를 가리키는 문서

아직 없습니다.

뜻이 가까운 노트

  • type과 interface

    type(타입 별칭, Type Alias)과 interface는 둘 다 타입에 이름을 붙이는 방법이다. 객체 모양을 정의할 때는 대부분 바꿔 써도 되지만, 확장 방식과 표현 범위가 다르다.

  • 유니언·인터섹션·리터럴 타입

    유니언 타입(Union Type, A | B)은 "A이거나 B인 값"이고, 인터섹션 타입(Intersection Type, A & B)은 "A이면서 B인 값"이다. 리터럴 타입(Literal Type)은 'left'나 3처럼 특정 값 하나만 허용하는 타입이다.

  • 함수 오버로드와 시그니처

    함수 오버로드(Function Overloads)는 구현 하나 위에 호출 방식별 시그니처를 여러 개 선언해, 인자 조합에 따라 정확한 반환 타입이 추론되게 하는 TypeScript 기능이다.

  • TypeScript 개요

    TypeScript는 JavaScript에 정적 타입(Static Typing)을 더한 슈퍼셋(superset) 언어다. 브라우저는 TS를 그대로 실행하지 못하므로, 타입을 검사한 뒤 타입 표기를 지운 JavaScript로 컴파일된다. 타입 표기는 프로그램의 런타임 동작을 바꾸지 않는다.

  • satisfies 연산자

    satisfies(TypeScript 4.9+)는 값이 어떤 타입을 만족하는지 검사하되, 값에서 추론된 구체적인 타입은 그대로 유지하는 연산자다.

보기 옵션