📑 목차
타입스크립트에는 기본 타입을 상황에 따라 유동적으로 변환할 수 있는 강력한 기능들이 있다. 제네릭(Generic)도 그중 하나지만, 이번에는 제네릭과 조건부 타입을 제외한 4가지 핵심 타입 조작 기능을 정리해 본다.
- 인덱스드 엑세스 타입 (Indexed Access Types)
- keyof 연산자
- 맵드 타입 (Mapped Types)
- 템플릿 리터럴 타입 (Template Literal Types)
1. 인덱스드 엑세스 타입 (Indexed Access Types)
인덱스를 이용해 다른 타입 내의 특정 프로퍼티 타입을 추출하는 기능이다. 객체, 배열, 튜플에 모두 사용할 수 있다.
1-1. 객체 프로퍼티 타입 추출
게시글(Post) 타입에서 작성자(author)의 타입만 따로 떼어내고 싶을 때 유용하다.
interface Post {
title: string;
content: string;
author: {
id: number;
name: string;
age: number;
};
}
// Post["author"]를 통해 author 프로퍼티의 타입을 추출
function printAuthorInfo(author: Post["author"]) {
console.log(`${author.id} - ${author.name}`);
}
이렇게 하면 Post 타입이 수정되더라도 printAuthorInfo 함수의 매개변수 타입을 일일이 수정할 필요가 없다.
주의할 점:
- 인덱스에는 값이 아닌 타입만 들어갈 수 있다. 변수에 담긴 문자열(
const key = "author")을 인덱스로 쓸 수 없다. - 존재하지 않는 프로퍼티 키를 넣으면 오류가 발생한다.
1-2. 배열 요소 타입 추출
배열 타입 뒤에 [number]를 붙이면 배열 요소의 타입을 추출할 수 있다.
type PostList = {
title: string;
content: string;
author: {
id: number;
name: string;
age: number;
};
}[];
// 배열의 요소 타입 추출
const post: PostList[number] = {
title: "게시글 제목",
content: "게시글 본문",
author: {
id: 1,
name: "조태민",
age: 25,
},
};
PostList[0]처럼 리터럴 숫자를 넣어도 동일하게 동작한다.
2. keyof 연산자
객체 타입으로부터 모든 프로퍼티의 키(Key)를 String Literal Union 타입으로 추출하는 연산자다.
interface Person {
name: string;
age: number;
location: string;
}
// keyof Person 결과: "name" | "age" | "location"
function getPropertyKey(person: Person, key: keyof Person) {
return person[key];
}
프로퍼티가 추가되거나 이름이 바뀌어도 keyof가 자동으로 반영해주므로 유지보수가 매우 편해진다.
꿀팁: typeof와 함께 사용하기
변수의 타입을 가져오는 typeof 연산자와 함께 사용하면 객체 변수로부터 타입을 뽑아내고, 그 키들을 추출할 수 있다.
const person = {
name: "조태민",
age: 25,
};
// typeof person -> Person 객체 타입 추론
// keyof typeof person -> "name" | "age"
function getPropertyKey(person: typeof person, key: keyof typeof person) {
return person[key];
}
3. 맵드 타입 (Mapped Types)
기존 객체 타입을 기반으로 새로운 객체 타입을 만드는 기능이다. 자바스크립트의 map 함수와 비슷하게 동작한다.
예를 들어, 모든 프로퍼티를 선택적(?)으로 바꾸는 PartialUser 타입을 만들어보자.
interface User {
id: number;
name: string;
age: number;
}
// 맵드 타입 정의
type PartialUser = {
[key in keyof User]?: User[key];
};
[key in keyof User]:User의 키들(id|name|age)을 순회한다.User[key]: 해당 키의 타입을 가져온다.?: 모든 프로퍼티를 선택적 속성으로 만든다.
결과적으로 PartialUser는 다음과 같은 타입이 된다 5.
{
id?: number;
name?: string;
age?: number;
}
이를 활용하면 모든 속성을 읽기 전용(readonly)으로 만드는 등 다양하게 변형할 수 있다.
type ReadonlyUser = {
readonly [key in keyof User]: User[key];
};
4. 템플릿 리터럴 타입 (Template Literal Types)
문자열 리터럴 타입을 조합하여 새로운 문자열 패턴 타입을 만드는 기능이다.
type Color = "red" | "black" | "green";
type Animal = "dog" | "cat" | "chicken";
// 모든 경우의 수 조합 (ex: "red-dog", "black-cat" ...)
type ColoredAnimal = `${Color}-${Animal}`;
일일이 조합을 적지 않아도 Color와 Animal의 모든 가능한 조합을 자동으로 생성해 준다 6.
요약
| 기능 | 설명 | 예시 문법 |
|---|---|---|
| 인덱스드 엑세스 | 특정 프로퍼티/요소의 타입 추출 | Type["key"], ArrayType[number] |
| keyof | 객체 타입의 키를 유니온으로 추출 | keyof Type |
| 맵드 타입 | 기존 타입을 순회하며 변형 | [Key in keyof Type]: Type[Key] |
| 템플릿 리터럴 | 문자열 패턴 조합 | ${TypeA}-${TypeB} |
이 기능들을 적절히 활용하면 중복 코드를 줄이고, 유지보수하기 쉬운 유연한 타입을 설계할 수 있다.
