Menu

TypeScript 인덱스 접근 타입: T[K], T[number] 등

인덱스 접근 타입은 다른 타입에서 속성의 타입을 읽어 옵니다. Person["age"]는 number입니다. T["key"], 유니언 키, T[keyof T], 배열 요소를 위한 T[number], 튜플 인덱스, const 배열을 유니언으로 바꾸는 (typeof arr)[number]를 알아봅니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

인덱스 접근 타입은 JavaScript가 속성 접근에 쓰는 것과 같은 대괄호 문법으로 다른 타입에서 속성의 타입을 조회합니다. Person["age"]는 number이고, Person["address"]["city"]는 string입니다.

장점은 진실의 원천이 하나라는 것입니다. Age는 number의 두 번째 복사본이 아닙니다. Person["age"]가 string으로 바뀌면 Person["age"]로 쓴 모든 타입이 함께 바뀝니다. 인덱스 접근 타입은 컴파일 시점에만 존재하며 JavaScript를 출력하지 않습니다.

유니언 키와 T[keyof T]

인덱스는 키의 유니언일 수 있습니다. 결과는 속성 타입들의 유니언입니다.

Product[keyof Product]는 "Product의 아무 속성 값"을 타입 수준에서 말하는 방법입니다. 존재하지 않는 키를 요청하면 컴파일 오류입니다. Product["price"]는 error TS2339: Property 'price' does not exist on type 'Product'를 줍니다.

선택적 속성에는 undefined가 포함된다

strictNullChecks(strict에서 켜짐)가 켜져 있으면 선택적 속성의 타입에 undefined가 포함되고, 그 인덱스 접근 타입도 마찬가지입니다.

type Profile = { name: string; nickname?: string };

type Nick = Profile["nickname"]; // string | undefined

undefined를 빼려면 감싸세요. NonNullable<Profile["nickname"]>은 string입니다.

T[number]로 배열 요소 타입 얻기

배열은 숫자로 인덱싱되므로, 배열 타입을 number로 인덱싱하면 요소 타입을 얻습니다. 여러 단계를 거쳐서도 동작합니다.

생성된 API 클라이언트처럼 직접 제어할 수 없는 곳에서 온 타입일 때 유용합니다. ApiResponse["data"]["users"][number]는 아무도 export하지 않은 사용자 타입에 이름을 붙여 줍니다.

튜플: 위치로 인덱싱하기

튜플 타입은 특정 위치로, 모든 요소 타입의 유니언을 위한 number로, 그리고 길이를 리터럴 타입으로 얻는 "length"로 인덱싱할 수 있습니다.

(typeof arr)[number]: const 배열에서 유니언 만들기

실제로 가장 흔한 용도입니다. 허용되는 값의 목록을 배열로 두고(런타임에 순회할 수 있도록), 그 배열에서 유니언 타입을 끌어냅니다(컴파일러가 검사하도록).

두 부분이 이를 가능하게 합니다. as const는 배열을 리터럴 타입의 readonly 튜플로 만듭니다. 없으면 ROLES는 string[]이고 (typeof ROLES)[number]는 그냥 string입니다. 그다음 typeof가 값을 타입으로 바꾸고, [number]가 그 요소들을 유니언으로 모읍니다. 괄호는 선택 사항이지만(typeof ROLES[number]도 같은 뜻), 연산 순서를 분명하게 해 줍니다.

문자열이 배열에 있는지 확인하기

이 readonly 튜플에는 의외의 점이 하나 있습니다. includes 메서드가 요소 타입만 받으므로 임의의 문자열을 넘길 수 없습니다.

index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.

검사할 때는 배열을 readonly string[]로 넓히고, 검사에 성공하면 값이 좁혀지도록 함수를 타입 가드로 만드세요.

이 패턴은 두 가지 일에 목록 하나를 씁니다. 런타임 검사는 배열을 쓰고, 타입은 그 배열에서 끌어내므로 둘이 어긋날 수 없습니다.

제네릭의 인덱스 접근: T[K]

제네릭 안에서 K extends keyof T와 함께 쓰는 T[K]는 호출하는 쪽이 고른 속성의 타입입니다.

function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map((item) => item[key]);
}

const users = [
  { name: "Ada", age: 36 },
  { name: "Linus", age: 28 },
];

const names = pluck(users, "name"); // string[]
const ages = pluck(users, "age");   // number[]

K extends keyof T 제약이 T[K]를 합법으로 만듭니다. 이것이 없으면 TypeScript는 K가 T의 키라는 것을 알 수 없어서 error TS2536: Type 'K' cannot be used to index type 'T'를 보고합니다.

문법 한눈에 보기

쓴 것의미결과의 예
T["key"]속성 하나의 타입Person["age"]는 number
T["a" | "b"]그 속성 타입들의 유니언string | number
T[keyof T]모든 속성 타입의 유니언모든 값 타입
T["a"]["b"]중첩 속성Person["address"]["city"]
Arr[number]배열 요소 타입User[]는 User
Tup[0]특정 위치의 튜플 요소첫 번째 요소 타입
Tup["length"]리터럴로 된 튜플 길이3
(typeof arr)[number]const 배열 값들의 유니언"admin" | "editor"
T[typeof key]상수의 타입으로 인덱싱T["name"]과 같음

인덱스는 항상 타입입니다. key가 const 변수일 때 Person[key]는 두 오류로 실패합니다: error TS2538: Type 'key' cannot be used as an index type.와 error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.

자주 묻는 질문

TypeScript에서 인덱스 접근 타입이란 무엇인가요?

타입 T에서 속성 K의 타입을 조회하는 T[K] 형태의 타입입니다. type Person = { name: string; age: number }라면 Person["age"]는 number입니다. JavaScript의 속성 접근과 같은 대괄호 문법을 쓰지만, 컴파일 시점에 타입에 대해 동작합니다.

TypeScript에서 배열 요소의 타입은 어떻게 얻나요?

배열 타입을 number로 인덱싱하세요. type Users = User[]라면 Users[number]는 User입니다. 배열 값이라면 typeof와 조합합니다: (typeof users)[number].

문자열 배열을 유니언 타입으로 바꾸려면 어떻게 하나요?

TypeScript가 리터럴 값을 유지하도록 배열을 as const로 선언한 다음 number로 인덱싱하세요. const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number];은 "admin" | "editor"가 됩니다. as const가 없으면 요소 타입은 그냥 string입니다.

인덱스 접근 타입에서 변수를 인덱스로 쓸 수 없는 이유는 무엇인가요?

인덱스는 타입이어야 합니다. const key = "name"일 때 Person[key]는 key가 값이므로 실패합니다(오류 TS2538과 TS2749). Person[typeof key]라고 쓰거나, 리터럴을 직접 쓰세요: Person["name"].

T[keyof T]는 무슨 뜻인가요?

T를 모든 키의 유니언으로 인덱싱하므로, 결과는 모든 속성 타입의 유니언입니다. { a: string; b: number }라면 string | number입니다. "이 객체의 아무 값"을 타입 수준에서 말하는 방법입니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기