as const는 값의 타입을 가장 좁은 리터럴 타입으로 고정 하고, 읽기 전용(readonly) 으로 만드는 단언입니다.

기본 동작

TYPESCRIPT
// 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는 세 가지를 동시에 합니다:

  1. 모든 속성을 리터럴 타입 으로 좁힌다
  2. 모든 속성에 readonly 를 붙인다
  3. 배열은 튜플 로 변환한다

배열에서의 as const

TYPESCRIPT
// 일반 배열 — 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

배열에서 유니온 추출

TYPESCRIPT
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

TYPESCRIPT
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'

양방향 매핑

TYPESCRIPT
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

TYPESCRIPT
// 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'

함수 매개변수에서의 활용

TYPESCRIPT
// 함수 호출 시 인수가 넓은 타입으로 추론되는 문제
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)

TYPESCRIPT
// 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) 곳까지 적용됩니다.

TYPESCRIPT
const data = {
  users: [
    { name: '홍길동', roles: ['admin', 'user'] },
  ],
} as const;

// data.users[0].roles: readonly ['admin', 'user']
// data.users[0].name: '홍길동'

변수에 as const

TYPESCRIPT
// 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 제네릭으로 함수 호출 시에도 리터럴 추론이 가능하다
댓글 로딩 중...