TypeScript에서 as const (const assertion) 는 특정 값이 변경되지 않음을 TypeScript에게 알려주는 역할을 한다. 이를 사용하면 TypeScript가 해당값을 가능한 가장 좁은 타입(narrow type)으로 추론하게 되는데, 이게 도대체 무슨 말인지 차근차근 알아보자.
일반적으로 변수를 선언하면 TypeScript는 해당 변수를 보다 넓은 타입으로 추론한다.
let foo = 'foo';
// let foo: string
하지만 as const를 추가하면, TypeScript는 이 값을 리터럴 타입(literal type)으로 고정한다.
let foo = 'foo' as const;
// let foo: 'foo'
숫자의 경우도 마찬가지이다.
let num = 10 as const;
// let num: 10
그러면 const 키워드와 똑같은 것 아닌가? 라고 생각이 들지만 둘은 객체나 배열에 사용할 때 큰 차이가 있다.
const obj = { key: 'value' };
// const obj: { key: string }
let obj2 = { key: 'value' } as const;
// let obj: { readonly key: 'value' }
as const를 사용하면 객체의 속성이 읽기 전용(readonly)이 되어 값을 변경할 수 없다.
obj2.key = 'new value';
// 오류: 'key'는 읽기 전용 속성입니다.
객체에 as const를 적용하면 모든 속성이 readonly가 되며, 각 속성의 타입이 리터럴 값으로 고정된다.
const myObject = {
name: 'Alice',
age: 25,
} as const;
// const myObject: { readonly name: 'Alice'; readonly age: 25; }
myObject.age = 30; // 오류 발생 (읽기 전용 속성)
배열에 as const를 적용하면 튜플(tuple)처럼 동작하며, 각 요소의 타입이 리터럴로 고정된다.
튜플이란, 배열과 비슷하지만 요소의 개수와 타입이 고정된 자료구조이다.
as const를 사용하면 배열의 요소가 변경되지 않으며, 순서까지 보장된다.
const colors = ['red', 'green', 'blue'] as const;
// const colors: readonly ['red', 'green', 'blue']
colors[0] = 'yellow'; // 오류 발생 (읽기 전용 배열)
colors.push('yellow'); // 오류 발생 (push 사용 불가)
as const는 유니온 타입을 생성할 때 유용하게 사용할 수 있다.
type ButtonVariant = 'primary' | 'secondary' | 'danger';
const buttonVariants = ['primary', 'secondary', 'danger'] as const;
// 배열을 `as const`로 선언하면 각 요소가 리터럴 타입으로 고정됨
// 따라서 `typeof buttonVariants[number]`를 사용하면 각 요소의 타입을 추출하여 유니온 타입을 만들 수 있음
type ButtonVariantFromArray = typeof buttonVariants[number];
// 'primary' | 'secondary' | 'danger'
이와 같이 buttonVariants 배열에 as const를 적용하면, ButtonVariant라는 타입을 별도로 정의하지 않아도 buttonVariants 배열에서 자동으로 유니온 타입을 생성할 수 있다.
React의 useState를 사용할 때 as const를 사용하면 더 정확한 타입 추론이 가능하다.
function useToggle() {
const [isActive, setIsActive] = useState(false);
const toggle = () => setIsActive(prev => !prev);
return [isActive, toggle] as const;
}
const [isActive, toggle] = useToggle();
// isActive: boolean
// toggle: () => void
as const가 없으면 useToggle의 반환값이 (boolean | (() => void))[]으로 추론된다. 즉, 배열 요소의 순서나 타입이 정확하게 유지되지 않는다.
하지만 as const를 사용하면 튜플 타입이 유지되어 반환된 값의 순서와 타입이 정확하게 보장된다. 즉, isActive는 항상 boolean, toggle은 항상 () => void 타입을 가지므로 타입 안정성이 더욱 높아진다.
TypeScript의 as const는 변수의 값을 변경할 수 없도록 고정하면서, 타입을 가능한 가장 좁은 범위로 제한하는 기능이다. 특히 객체와 배열을 읽기 전용으로 만들거나, 유니온 타입을 생성할 때 유용하다.
정리하면, 아래와 같은 경우에 as const를 적절히 활용하면 안전한 코드 작성과 타입 안정성을 높일 수 있다!
React의 useState와 같은 훅을 사용할 때 튜플 형태로 반환값을 보장하고 싶을 때