TypeScript satisfies는 타입 지정 및 as const와 어떻게 다른가요?
satisfies는 객체가 필요한 타입 조건을 만족하는지 검사하면서 객체 자체의 구체적인 타입 정보를 보존할 때 사용합니다. 타입 지정이나 as const와 역할이 같지는 않습니다.
적용 조건·범위: 이 코드는 외부 JSON이 실제 Config인지 runtime에서 검증하지 않습니다.
공식 문서 참고: TypeScript: satisfies의 구조 검사와 구체적 추론 보존
직접 확인: 오류에서 수정까지
TypeScript satisfies 사용법: 타입 지정·as const와 비교하기 · 독립 실습
얻을 결과: satisfies Record<Filter, string>으로 키 전체를 검사하고 실제 객체 추론을 유지합니다.
예제 크게 보기 (새 탭)단독 검사는 tsconfig.json이 없는 새 폴더에서 example.ts 한 파일씩 교체하며 진행합니다. TypeScript가 없다면 설치 글에서 Node.js/npm·TypeScript 준비를 확인하세요. 타입 검사 성공은 코드 실행 성공과 구분합니다.
독자 질문: 필터 설정의 빠진 키와 잘못된 값을 어떻게 찾을까요?
얻을 결과: satisfies Record<Filter, string>으로 키 전체를 검사하고 실제 객체 추론을 유지합니다.
오류 코드를 별도 example.ts에 복사합니다.
type Filter = 'all' | 'done';
const labels = { all: '전체' } satisfies Record<Filter, string>;
예상 타입 오류 (TS2741): Record<Filter, string>에 필요한 done 속성이 없습니다. 누락된 라벨을 추가합니다.
위 코드를 다음 수정 예제로 교체합니다.
type Filter = 'all' | 'done';
const labels = { all: '전체', done: '완료' } satisfies Record<Filter, string>;
console.log(labels.done);
npx tsc --strict --noEmit example.ts
확인 결과: TypeScript 7.0.2의 별도 최소 예제에서 오류 TS2741, 수정 후 exit 0 확인. TaskFlow 전체 체크포인트·브라우저 실행은 미확인.
다음 학습: 10회차에서 제네릭으로 입력과 출력 타입의 관계를 유지합니다.
annotation 방식
type Palette = {
primary: string;
danger: string;
};
const colors:
Palette = {
primary: '#111111',
danger: '#b91c1c',
};
colors는 Palette로 취급됩니다.
구조 검사는 잘 되지만 객체의 더 구체적인 정보가 넓어질 수 있습니다.
satisfies
const colors = {
primary: '#111111',
danger: '#b91c1c',
} satisfies Palette;
객체가 Palette 조건을 만족하는지 검사하면서 객체가 가진 구체적인 property 정보를 유지합니다.
잘못된 key:
const colors = {
primary: '#111111',
error: '#b91c1c',
} satisfies Palette;
danger가 빠지고 error가 추가됐으므로 오류가 발생합니다.
Record와 조합
type Filter =
| 'all'
| 'active'
| 'completed';
const labels = {
all: '전체',
active: '진행 중',
completed: '완료',
} satisfies Record<
Filter,
string
>;
모든 Filter key가 존재해야 합니다.
as const와 함께
const routes = {
home: '/',
tasks: '/tasks',
} as const satisfies Record<
string,
string
>;
as const는 literal 보존과 readonly에 영향을 주고, satisfies는 타입 조건을 검사합니다.
두 문법의 역할이 다릅니다.
satisfies는 runtime 검증이 아닙니다
type Config = { enabled: boolean };
const text = '{"enabled":"잘못된 값"}';
const config =
JSON.parse(text)
satisfies Config;
이 코드는 외부 JSON이 실제 Config인지 runtime에서 검증하지 않습니다.
satisfies는 compile-time 문법입니다.
외부 데이터는 16회차에서 type guard로 검증합니다.
독립 예제
type HttpMethod =
| 'GET'
| 'POST';
type Endpoint = {
path: string;
method: HttpMethod;
};
const endpoints = {
list: {
path: '/tasks',
method: 'GET',
},
create: {
path: '/tasks',
method: 'POST',
},
} satisfies Record<
string,
Endpoint
>;
잘못된 method를 적으면 컴파일 단계에서 찾을 수 있습니다.
학습 완료 체크리스트
- type annotation과 satisfies 차이를 설명할 수 있습니다.
- satisfies로 객체 구조를 검증할 수 있습니다.
- as const와 satisfies를 함께 사용할 수 있습니다.
- Record와 결합해 key 누락을 확인할 수 있습니다.
- satisfies가 runtime validation이 아님을 이해했습니다.
작은 변형으로 확인하기
done 값을 숫자 1로 바꾸면 string 조건 오류가 발생합니다. satisfies를 통과했다는 사실이 JSON 입력을 실행 시 검사한다는 뜻인지 설명하세요.
선택 실습: 할 일 앱에 적용하기
TaskFlow는 작업을 추가·완료·삭제하는 할 일 앱입니다. 이 선택 실습에서는 JavaScript 코드를 TypeScript로 확장합니다.
TaskFilter label과 설정 map의 key 누락을 satisfies로 검사합니다.
선택: 연재 프로젝트와 전체 자료
연재 프로젝트를 이어 만드는 분을 위한 자료입니다. 이 글의 개념과 예제만 찾으셨다면 필요한 파일과 실행 방법만 펼쳐보세요.
TaskFlow 프로젝트 적용
TypeScript satisfies 사용법: 타입 지정·as const와 비교하기 · 단계별 TaskFlow
타입을 적용한 TaskFlow에서 작업을 추가·완료 전환·삭제하세요. 타입만 바뀐 단계는 이전과 화면이 같습니다.
예제 크게 보기 (새 탭)변경 설명과 부분 예제를 읽은 뒤, 시작·완료 파일과 적용 순서에서 필요한 선언·import·화면 연결을 포함한 전체 파일을 확인하세요. 이번 단계의 변경 이유와 적용 위치도 함께 확인합니다.
시작본과 이번 변경
TypeScript as const 사용법: union 타입을 자동 생성하는 기준의 완료 체크포인트를 기준으로 이어갑니다. 변경하지 않는 파일은 그 시작 상태를 유지합니다.
src/constants/task.ts의 TASK_FILTER_LABELS에 satisfies를 적용합니다. 라벨 하나를 빠뜨리거나 값에 숫자를 넣으면 오류가 나는지 확인하고 정상 라벨로 되돌립니다.
이전 체크포인트
typescript-step-08-as-const
이번 목표
TaskFilter label과 설정 map의 key 누락을 satisfies로 검사합니다.
전체 파일·코드 보기
src/constants/task.ts
export const TASK_FILTERS = [
'all',
'active',
'completed',
] as const;
export type TaskFilter =
typeof TASK_FILTERS[number];
export const TASK_FILTER_LABELS = {
all: '전체',
active: '진행 중',
completed: '완료',
} satisfies Record<
TaskFilter,
string
>;
설정 map
type SettingKey =
| 'compact'
| 'showCompleted';
type SettingInfo = {
label: string;
};
export const settings = {
compact: {
label: '간단히 보기',
},
showCompleted: {
label: '완료 작업 표시',
},
} satisfies Record<
SettingKey,
SettingInfo
>;
key가 하나라도 빠지면 오류가 발생합니다.
검증
npx tsc --noEmit
중간부터 시작하는 전체 파일과 적용 순서
시작 ZIP은 직전 프로젝트 단계 typescript-step-08-as-const의 실제 완료 파일입니다. 프로젝트 순서를 이어가는 기준이며 사이트의 기술 읽기 순서와는 구분합니다.
- 시작 ZIP을 새 폴더에 풉니다. 이 폴더가 작업 루트이며 기존 체크포인트 파일을 별도로 찾을 필요가 없습니다.
- 라벨과 설정 맵에 satisfies를 적용해 키 누락과 값 타입을 검사합니다.
- 아래 변경 파일을 같은 상대 경로에 추가하거나 전체 교체합니다. 표시하지 않은 파일과 images는 시작본 그대로 유지합니다. package-lock.json은 완료 ZIP에 있는 파일을 함께 적용합니다.
- 작업 루트에서 명령을 실행하고 예상 결과를 확인합니다. 완료 ZIP은 직접 적용한 결과를 비교하는 기준입니다.
추가·교체 파일
CHANGES.md— 전체 교체README.md— 전체 교체tests/contracts.ts— 전체 교체src/constants/task.ts— 전체 교체src/constants/settings.ts— 추가
실행과 확인
npm ci
npm run dev
npm run typecheck
npm test
npm run build
개발 서버는 실행 상태로 두고 다른 터미널에서 검사 명령을 실행하세요. Node.js 24.18.0에서 검증했습니다. JavaScript는 http://127.0.0.1:4173, TypeScript는 Vite 실행 출력의 주소로 접속합니다.
예상 결과: 기존 화면은 유지하고 잘못된 맵 키 접근은 컴파일에서 거부됩니다.
실제 검증: TypeScript strict 검사·긍정/부정 타입 계약·런타임 검사·Vite 빌드 통과. 시작 ZIP에 이 글의 변경 파일만 적용한 결과와 완료 ZIP의 모든 파일 내용이 일치했습니다. 브라우저 화면 배치·실제 기기·외부 CDN 가용성은 이 검사에 포함하지 않았습니다.
이 절의 전체 파일은 ZIP과 일치합니다. 위쪽 원문의 짧은 예제나 생략 표시가 있는 부분 코드는 개념·변경 위치 설명용입니다. 서버 저장과 인증은 제공하지 않으며 JSON fixture는 로컬 읽기 실습용입니다.
복사할 변경 파일 전체 코드
tests/contracts.ts — 전체 파일
import type { Task } from '../src/types/task.js';
const valid: Task = {id:'1',title:'공부',completed:false};
// @ts-expect-error completed must be boolean
const invalid: Task = {id:'1',title:'공부',completed:'no'};
void valid; void invalid;
import { readTitle } from '../src/core/selection.js';
const nullable = readTitle(null);
// @ts-expect-error null result must be narrowed
const definite: string = nullable;
void definite;
import type { TaskKey } from '../src/types/task-key.js';
// @ts-expect-error nonexistent property
const badKey: TaskKey = 'missing';
void badKey;
import { TASK_FILTER_LABELS } from '../src/constants/task.js';
// @ts-expect-error missing is not a filter key
const badLabel = TASK_FILTER_LABELS.missing;
void badLabel;
src/constants/task.ts — 전체 파일
export const TASK_FILTERS = [
'all',
'active',
'completed',
] as const;
export type TaskFilter =
typeof TASK_FILTERS[number];
export const TASK_FILTER_LABELS = {
all: '전체',
active: '진행 중',
completed: '완료',
} satisfies Record<
TaskFilter,
string
>;
src/constants/settings.ts — 전체 파일
type SettingKey =
| 'compact'
| 'showCompleted';
type SettingInfo = {
label: string;
};
export const settings = {
compact: {
label: '간단히 보기',
},
showCompleted: {
label: '완료 작업 표시',
},
} satisfies Record<
SettingKey,
SettingInfo
>;
완료 체크포인트
typescript-step-09-satisfies
다음 학습
다음 글에서는 타입을 매개변수처럼 받아 재사용하는 generic을 함수와 API 응답 형태로 익힙니다.
다음 글: TypeScript 제네릭 쉽게 이해하기: props와 API 응답 타입으로 감 잡기
글 소개·학습 준비·전체 목차

TypeScript · 9 / 22 · 필수 학습 · satisfies
satisfies는 객체가 특정 타입 조건을 만족하는지 검사하면서, 객체 자체의 구체적인 타입 정보는 가능한 한 그대로 보존하고 싶을 때 유용합니다.
type annotation과 as const 사이에서 어떤 역할을 하는지 비교하면 이해하기 쉽습니다.
이번 글에서 배우는 내용
- type annotation
- satisfies
- as const와의 차이
- Record와 함께 사용하는 패턴
- 구체적인 literal 추론 보존
- runtime validation과의 차이
단독 학습 선수 지식
객체 타입·as const·Record의 키와 값 관계을 알면 됩니다. 이전 TaskFlow 파일 없이 직접 확인 예제로 학습할 수 있습니다.
목차
이 글이 도움이 되었나요?
읽음 기록
읽음 — / 전체 22개 · 필수 21개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.