const assertion — as const로 리터럴 타입 고정하기
as const는 값의 타입을 가장 좁은 리터럴 타입으로 고정 하고, 읽기 전용(readonly) 으로 만드는 단언입니다.
기본 동작
// as const 없이 — 넓은 타입으로 추론
const config = {
host: 'localhost', // string
port: 3000, // number
debug: true, // boolean
};
// as const — 리터럴 타입으로 고정
const configConst = {
host: 'localhost', // 'localhost'
port: 3000, // 3000
debug: true, // true
} as const;
// 타입: { readonly host: 'localhost'; readonly port: 3000; readonly debug: true }
as const는 세 가지를 동시에 합니다:
- 모든 속성을 리터럴 타입 으로 좁힌다
- 모든 속성에 readonly 를 붙인다
- 배열은 튜플 로 변환한다
배열에서의 as const
// 일반 배열 — number[]
const numbers = [1, 2, 3];
// as const — readonly [1, 2, 3] (튜플)
const tuple = [1, 2, 3] as const;
// tuple.push(4); // ❌ Error — readonly
// tuple[0] = 10; // ❌ Error — readonly
배열에서 유니온 추출
const ROLES = ['admin', 'user', 'guest'] as const;
// 배열에서 유니온 타입 추출
type Role = (typeof ROLES)[number];
// 'admin' | 'user' | 'guest'
// 함수 매개변수로 사용
function setRole(role: Role) {
console.log(`역할: ${role}`);
}
setRole('admin'); // OK
// setRole('superadmin'); // ❌ Error
이 패턴은 면접에서도 자주 물어봅니다. "배열 값을 유니온 타입으로 변환하는 방법"의 정답입니다.
객체에서의 as const
const HTTP_STATUS = {
OK: 200,
NOT_FOUND: 404,
INTERNAL_ERROR: 500,
} as const;
// 값들을 유니온으로 추출
type StatusCode = (typeof HTTP_STATUS)[keyof typeof HTTP_STATUS];
// 200 | 404 | 500
// 키들을 유니온으로 추출
type StatusName = keyof typeof HTTP_STATUS;
// 'OK' | 'NOT_FOUND' | 'INTERNAL_ERROR'
양방향 매핑
const COLOR_MAP = {
red: '#ff0000',
green: '#00ff00',
blue: '#0000ff',
} as const;
type ColorName = keyof typeof COLOR_MAP;
// 'red' | 'green' | 'blue'
type ColorHex = (typeof COLOR_MAP)[ColorName];
// '#ff0000' | '#00ff00' | '#0000ff'
enum 대안으로서의 as const
// enum
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT',
}
// as const 대안 — 트리쉐이킹 가능
const Direction = {
Up: 'UP',
Down: 'DOWN',
Left: 'LEFT',
Right: 'RIGHT',
} as const;
type Direction = (typeof Direction)[keyof typeof Direction];
// 'UP' | 'DOWN' | 'LEFT' | 'RIGHT'
함수 매개변수에서의 활용
// 함수 호출 시 인수가 넓은 타입으로 추론되는 문제
function request(method: 'GET' | 'POST', url: string) {}
const config = { method: 'GET', url: '/api' };
// request(config.method, config.url); // ❌ Error — method: string
// 해결 1: as const를 객체 전체에
const config1 = { method: 'GET', url: '/api' } as const;
request(config1.method, config1.url); // OK
// 해결 2: 특정 속성에만 as const
const config2 = { method: 'GET' as const, url: '/api' };
request(config2.method, config2.url); // OK
제네릭에서의 const 타입 매개변수 (TS 5.0)
// TS 5.0: const 제네릭으로 호출 시 리터럴 추론 강제
function createConfig<const T extends Record<string, unknown>>(config: T): T {
return config;
}
// as const 없이도 리터럴 타입으로 추론됨
const config = createConfig({
host: 'localhost', // 'localhost' (리터럴)
port: 3000, // 3000 (리터럴)
});
주의점
깊은 as const
as const는 깊은(deep) 곳까지 적용됩니다.
const data = {
users: [
{ name: '홍길동', roles: ['admin', 'user'] },
],
} as const;
// data.users[0].roles: readonly ['admin', 'user']
// data.users[0].name: '홍길동'
변수에 as const
// let과 함께 사용하면 재할당이 안 됨
let x = 'hello' as const; // x: 'hello'
// x = 'world'; // ❌ Error — 'world'는 'hello'에 할당 불가
// const와의 차이
const y = 'hello'; // y: 'hello' — const 자체가 리터럴로 추론됨
// 원시값에서는 as const를 안 써도 const가 같은 효과
정리
as const는 값을 리터럴 타입 + readonly로 만든다- 배열은 튜플로, 객체 속성은 리터럴 타입으로 고정된다
(typeof arr)[number]로 배열에서 유니온 타입을 추출할 수 있다- enum의 가벼운 대안으로
as const객체를 사용할 수 있다 - TS 5.0의
const제네릭으로 함수 호출 시에도 리터럴 추론이 가능하다
댓글 로딩 중...