숏컷 코드
type Route = {
path: string;
secure: boolean;
};
const routes = {
home: { path: "/", secure: false },
admin: { path: "/admin", secure: true },
} satisfies Record<string, Route>;리터럴 고정
이 카드에서 먼저 구분해 둘 기본형은 아래입니다.
- 계약 검사:
value satisfies Shape - 리터럴 고정:
value as const - 강제 단언:
value as Shape - 둘의 조합:
... as const satisfies Shape
1) 구조 계약 확인이 목적이면 satisfies
satisfies는 "이 값이 이 타입 계약을 만족하는가"를 검사하면서, 값 자체의 더 구체적인 정보는 최대한 유지하려고 합니다.
const config = {
mode: "dark",
} satisfies { mode: string };설정 객체나 라우트 테이블처럼 구조 검증과 값 보존이 둘 다 중요한 곳에서 특히 유용합니다.
2) 리터럴 정보를 가장 좁게 고정하면 as const
as const는 값 전체를 가장 좁은 리터럴 타입으로 고정하고, 객체/배열을 readonly로 만듭니다.
const roles = ["admin", "member"] as const;값에서 union 타입을 뽑거나 상수 맵을 안전하게 다룰 때 자주 씁니다.
3) as 단언과는 역할이 다르다
as는 타입을 덮어쓰는 쪽이고, satisfies는 실제 계약 만족 여부를 검사하는 쪽입니다.
즉 둘을 같은 종류의 도구로 보면 오해가 생깁니다.
4) 설정 객체에서는 둘을 조합할 수도 있다
값은 리터럴로 보존하고 싶고, 동시에 상위 계약도 확인하고 싶다면 as const와 satisfies를 함께 쓰는 경우도 있습니다.
이 카드의 핵심은 그 조합 자체보다 "무엇을 얻고 싶은지 먼저 구분하는 것"입니다.
const routes = {
home: { path: "/", secure: false },
admin: { path: "/admin", secure: true },
} as const satisfies Record<string, { path: string; secure: boolean }>;5) 넓은 타입으로 어설프게 먼저 선언하면 장점이 줄어든다
리터럴 보존이 필요한데 너무 일찍 string 같은 넓은 타입으로 선언해 버리면, 뒤에서 값 집합을 다시 추출하기 어려워집니다.
언제 satisfies를 붙일까
- 값이 특정 계약을 만족하는지 검사한다:
satisfies - 리터럴 정보와 readonly를 최대 보존한다:
as const - 강제로 덮어쓴다:
as - 설정 객체 구조 검증 + 값 구체성 유지:
satisfies - 값 배열에서 union을 뽑는다:
as const
주의할 점
as 타입 단언과 satisfies를 같은 용도로 보면 안 됩니다. as는 강제로 덮어쓰고, satisfies는 계약 만족 여부를 검사합니다.
// ❌ 구조가 맞는지 확인하지 않고 덮어씀
const config = { mode: "dark" } as { mode: string; retry: number };
// ✅ 실제 계약 만족 여부를 확인
const config = {
mode: "dark",
retry: 3,
} satisfies { mode: string; retry: number };참고 링크
2 sources