본문 바로가기
C.W.K.
Stream
Lesson 04 of 05 · published

as const: 리터럴의 정확한 모양을 고정하기

~10 min · arrays-tuples, as-const, literal-types, readonly

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"as const는 ‘이 값은 앞으로도 지금 적은 바로 그 값이다’라고 컴파일러에 말해."

세 가지 변화가 함께 일어나

리터럴 뒤에 as const를 붙이면 원시값이 넓은 타입으로 퍼지지 않고 정확한 리터럴 타입으로 남아. 객체 속성은 읽기 전용이 되고, 배열은 각 위치와 길이를 기억하는 읽기 전용 튜플이 된다.

['red', 'blue']는 보통 string 배열이지만 ['red', 'blue'] as constreadonly ['red', 'blue']야. 단순히 배열을 얼리는 기능이 아니라 정확성, 읽기 전용, 튜플 추론을 한꺼번에 요청하는 문법이지.

고정 목록에서 유니언을 뽑아

const ROLES = ['admin', 'editor', 'viewer'] as const를 선언한 뒤 type Role = typeof ROLES[number]라고 쓰면 세 문자열의 리터럴 유니언을 얻어. 실행 중 순회할 목록과 컴파일 시점 타입을 한 선언에서 파생하므로 둘이 어긋나지 않아.

설정 키, 지원 언어, 명령 이름처럼 실행에도 필요하고 타입에도 필요한 닫힌 목록에 이 패턴이 잘 맞아. 별도 enum과 배열을 각각 관리하는 중복을 없애지.

객체 설정과 판별 필드

설정 객체에 붙이면 각 속성의 값이 리터럴로 남아. 판별 가능한 유니언을 만들거나 경로 상수에서 실제 허용 경로 타입을 뽑을 때 유용해. 다만 객체 전체가 깊게 읽기 전용처럼 추론되므로 나중에 값을 바꿀 계획이라면 맞지 않아.

satisfies와의 차이

as const는 값을 최대한 좁고 읽기 전용으로 만든다. satisfies는 특정 계약을 만족하는지 검사하면서 원래 추론 정보를 보존해. 설정이 계약을 확인해야 하지만 모든 속성을 읽기 전용으로 만들고 싶지 않다면 satisfies가 더 알맞을 수 있어. 둘을 함께 쓰는 경우도 있다.

실행값이 곧 타입 목록의 원천이라면 as consttypeof X[number]를 써. 한 선언이 두 세계를 잇게 해.

피파의 고백

고정 목록과 유니언을 따로 적으면 언젠가 한쪽만 고치게 돼. 사람이 두 장부를 완벽히 맞추길 기대하는 대신 하나에서 다른 하나를 계산하게 만드는 편이 늘 낫더라.

Code

3 효과, 한 assertion·typescript
// 한 keyword 의 3 효과.

// 1. 값을 literal 타입으로 narrow.
const a = 'red';                     // a: string
const b = 'red' as const;             // b: 'red'

// 2. Property 동결.
const c = { color: 'red' };          // c: { color: string } — mutable
const d = { color: 'red' } as const; // d: { readonly color: 'red' }
d.color = 'blue';                    // ❌

// 3. 배열이 tuple 됨.
const e = ['a', 'b'];                // e: string[]
const f = ['a', 'b'] as const;       // f: readonly ['a', 'b']
f.push('c');                         // ❌ — 그리고 'c' 도 어차피 유효 아님
as const + typeof[number] — 기억할 가치 있는 패턴·typescript
// 헤드라인 패턴 — 단일 진실 source.

const BRAINS = ['claude', 'codex', 'gemini', 'ollama'] as const;
// BRAINS: readonly ['claude', 'codex', 'gemini', 'ollama']

type Brain = typeof BRAINS[number];
// Brain: 'claude' | 'codex' | 'gemini' | 'ollama'

// Runtime 사용:
for (const b of BRAINS) {
  console.log(b);   // 각 iteration 이 literal 타입 중 하나
}

// Type-system 사용:
function selectBrain(name: Brain) { /* ... */ }
selectBrain('claude');   // ✅
selectBrain('typo');     // ❌ — runtime 배열에서 literal-타입 체크

External links

Exercise

const STATUSES = ['idle', 'loading', 'done', 'error'] as const를 선언하고 typeof STATUSES[number]로 Status 타입을 파생해. 이어서 이 네 값만 받는 format(status: Status) 함수를 작성하고 다른 문자열이 거절되는지 확인해.
Hint
고정하지 않은 배열 원소는 일반 문자열로 넓어져. as const 뒤에는 각 위치의 리터럴이 보존되어 숫자 인덱스 접근으로 값 합집합을 만들 수 있어.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.