TypeScript 5 새 기능 — satisfies 연산자와 데코레이터 실전 활용
TypeScript 5의 핵심 신기능 완벽 가이드. satisfies 연산자로 타입 안전성 강화, 새로운 데코레이터 표준 구현, const 타입 파라미터, 다중 설정 파일 확장 등 실무에서 바로 쓸 수 있는 코드 예시 포함.
핵심 요약 TypeScript 5.0~5.4의 주요 신기능: ①
satisfies연산자 — 추론 유지하면서 타입 검증 ② 표준 데코레이터 — TC39 Stage 3 표준 준수, 메타데이터 지원 ③const타입 파라미터 — 리터럴 타입 추론 강화 ④ 다중 tsconfig 확장 ⑤ enum·namespace 개선. 실무에서 가장 파급력 큰 기능은satisfies와 새 데코레이터.
TypeScript 5 개요
TypeScript 5는 2023년 3월부터 시작된 주요 버전으로, 5.0~5.4까지 여러 마이너 업데이트가 누적되어 있습니다. 이 버전에서 가장 중요한 변화는 표준 데코레이터와 satisfies 연산자의 도입입니다.
버전별 주요 출시 일정:
| 버전 | 출시 | 주요 기능 |
|---|---|---|
| TypeScript 5.0 | 2023-03 | satisfies, const 타입 파라미터, 다중 tsconfig 확장 |
| TypeScript 5.1 | 2023-06 | getter/setter 독립 타입, JSX 개선 |
| TypeScript 5.2 | 2023-08 | using/await using 키워드, 데코레이터 메타데이터 |
| TypeScript 5.3 | 2023-11 | Import Attributes, 타입 좁히기 개선 |
| TypeScript 5.4 | 2024-03 | NoInfer 유틸리티, 클로저 타입 좁히기 보존 |
1. satisfies 연산자 — 실전 완전 정복
satisfies 연산자는 TypeScript 5.0에서 도입된 가장 혁신적인 기능 중 하나입니다. 기존 타입 단언의 한계를 넘어 타입 추론을 유지하면서 타입 제약을 검증합니다.
기존 문제점
// ❌ 기존 방식의 문제
type Color = "red" | "green" | "blue";
type ColorMap = Record<string, string | number[]>;
// as 단언: 타입 검증 O, 추론 X
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} as ColorMap;
// 이제 palette.red는 string | number[] 로 추론됨
// .map()을 쓰려면 타입 단언 필요
palette.red.map(x => x); // 오류! string | number[]에 map 없음satisfies 활용
// ✅ satisfies 연산자
type Color = "red" | "green" | "blue";
type ColorMap = Record<Color, string | number[]>;
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} satisfies ColorMap;
// 이제 타입 검증 O, 리터럴 추론 O
palette.red.map(x => x); // ✅ number[] 유지됨
palette.green.toUpperCase(); // ✅ string 유지됨
const wrong = { purple: "purple" } satisfies ColorMap; // ❌ 오류: "purple"은 Color 아님실전 활용 패턴
패턴 1: API 응답 객체 검증
interface ApiConfig {
baseUrl: string;
timeout: number;
retries: number;
}
// satisfies로 타입 검증 + 정확한 값 추론 유지
const config = {
baseUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
} satisfies ApiConfig;
// config.timeout은 number가 아닌 5000 (리터럴 타입)
type TimeoutType = typeof config.timeout; // 5000패턴 2: 라우트 정의
type Route = {
path: string;
component: React.ComponentType;
auth?: boolean;
};
const routes = [
{ path: "/", component: Home },
{ path: "/dashboard", component: Dashboard, auth: true },
] satisfies Route[];
// routes[0].path는 "/" (리터럴) 유지, Route 검증도 통과패턴 3: 다국어 번역 객체
type TranslationKey = "hello" | "goodbye" | "welcome";
type Translations = Record<TranslationKey, string>;
const ko = {
hello: "안녕하세요",
goodbye: "안녕히 가세요",
welcome: "환영합니다",
} satisfies Translations;
const en = {
hello: "Hello",
goodbye: "Goodbye",
// welcome 누락 → 컴파일 오류 발생! ✅
} satisfies Translations;2. 표준 데코레이터 — TC39 Stage 3 완전 구현
TypeScript 5.0은 오랫동안 사용하던 실험적 데코레이터(experimentalDecorators)와 별개로 TC39 Stage 3 표준 데코레이터를 지원합니다.
핵심 차이점
| 구분 | 실험적 데코레이터 | 표준 데코레이터 (5.0+) |
|---|---|---|
| 활성화 방법 | experimentalDecorators: true | 기본 활성화 (tsconfig 불필요) |
| 표준 준수 | 자체 구현 | TC39 Stage 3 |
| 실행 순서 | 역순 | 정순 |
| 메타데이터 | emitDecoratorMetadata | Symbol.metadata 사용 |
| 함수 지원 | 제한적 | 클래스 전용 |
클래스 데코레이터
// ✅ TypeScript 5 표준 데코레이터
function sealed(target: typeof SealedClass) {
Object.seal(target);
Object.seal(target.prototype);
}
@sealed
class SealedClass {
greet() { return "Hello!"; }
}메서드 데코레이터
function log(target: any, context: ClassMethodDecoratorContext) {
const methodName = String(context.name);
return function (this: any, ...args: any[]) {
console.log(`[${methodName}] 호출됨, 인수:`, args);
const result = target.call(this, ...args);
console.log(`[${methodName}] 반환값:`, result);
return result;
};
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
const calc = new Calculator();
calc.add(1, 2);
// [add] 호출됨, 인수: [1, 2]
// [add] 반환값: 3접근자 데코레이터
function readonly(target: any, context: ClassAccessorDecoratorContext) {
return {
set() {
throw new Error(`${String(context.name)}은 읽기 전용입니다.`);
}
};
}
class Config {
@readonly
accessor version = "1.0.0";
}
const cfg = new Config();
console.log(cfg.version); // "1.0.0"
cfg.version = "2.0.0"; // ❌ 오류 발생데코레이터 메타데이터 (TypeScript 5.2)
// 메타데이터 API 활용
function validate(target: any, context: ClassFieldDecoratorContext) {
context.metadata[context.name] = { required: true };
}
class UserForm {
@validate
name!: string;
@validate
email!: string;
}
// 메타데이터 읽기
const metadata = UserForm[Symbol.metadata];
console.log(metadata); // { name: { required: true }, email: { required: true } }3. const 타입 파라미터
TypeScript 5.0에서 추가된 const 수정자는 제네릭 함수의 타입 추론을 강화합니다.
// ❌ 기존 방식
function identity<T>(value: T): T {
return value;
}
const result = identity(["a", "b", "c"]);
// result: string[] (리터럴 손실)
// ✅ const 타입 파라미터
function identityConst<const T>(value: T): T {
return value;
}
const result2 = identityConst(["a", "b", "c"]);
// result2: readonly ["a", "b", "c"] (리터럴 유지!)실전 활용: 경로 빌더
function createRoute<const Path extends string>(path: Path) {
return { path, url: () => `https://app.example.com${path}` };
}
const homeRoute = createRoute("/");
// homeRoute.path: "/" (리터럴 유지)
const dashRoute = createRoute("/dashboard");
// dashRoute.path: "/dashboard" (리터럴 유지)4. 다중 tsconfig 확장
TypeScript 5.0부터 tsconfig.json의 extends에 배열을 사용할 수 있습니다.
// tsconfig.json
{
"extends": [
"@tsconfig/strictest/tsconfig.json",
"@tsconfig/node18/tsconfig.json",
"./custom.tsconfig.json"
],
"compilerOptions": {
"outDir": "dist"
}
}이전에는 체인 방식으로 여러 파일을 상속해야 했지만, 이제 배열로 한 번에 다중 상속이 가능합니다.
5. using / await using 키워드 (TypeScript 5.2)
자원 관리를 위한 새 키워드로, 자바의 try-with-resources와 유사합니다.
// Symbol.dispose가 있는 객체
class DbConnection {
constructor(public readonly id: string) {}
[Symbol.dispose]() {
console.log(`연결 ${this.id} 종료`);
}
}
function getConnection() {
return new DbConnection("conn-001");
}
// using: 블록 탈출 시 자동으로 dispose() 호출
{
using conn = getConnection();
console.log(`연결 ${conn.id} 사용 중`);
// 블록 종료 시 자동으로 "연결 conn-001 종료" 출력
}
// await using: 비동기 자원 해제
async function processFile() {
await using file = await openFile("data.txt");
// 처리 완료 후 자동으로 await file[Symbol.asyncDispose]() 호출
}6. NoInfer 유틸리티 타입 (TypeScript 5.4)
특정 위치의 타입 추론을 의도적으로 차단합니다.
// 문제: T가 기본값으로도 추론되어 타입이 넓어짐
function createState<T>(initial: T, fallback: T): T {
return initial ?? fallback;
}
// fallback이 초기값에 영향을 주면 안 될 때
const state = createState("active", "inactive");
// T: "active" | "inactive" (원하지 않을 수 있음)
// ✅ NoInfer 사용
function createStateBetter<T>(initial: T, fallback: NoInfer<T>): T {
return initial ?? fallback;
}
const state2 = createStateBetter("active", "inactive");
// T: "active", fallback은 "active" 타입으로 검증됨
// createStateBetter("active", "unknown") // 오류!마이그레이션 가이드
TypeScript 4.x → 5.x 전환 시 주의사항
| 변경 사항 | 영향 | 조치 |
|---|---|---|
| 실험적 데코레이터 유지 | 기존 코드 호환 | experimentalDecorators: true 유지 가능 |
| enum 상수 인라인 최적화 | 빌드 결과물 변경 가능 | 번들 크기 확인 |
tsconfig extends 배열 | 기존 문자열 방식 호환 | 변경 불필요 |
--verbatimModuleSyntax | 일부 import 오류 | 타입 전용 import 명시 필요 |
권장 tsconfig 설정 (2026 기준)
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"module": "Node16",
"moduleResolution": "Node16",
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"verbatimModuleSyntax": true
}
}💡 TypeScript 코드 작성 도구가 필요하신가요? 코드 가독성 분석에는 읽기 시간 계산기를 활용해보세요.
📣 대가성 안내: 이 포스팅에는 일부 도구 링크가 포함되어 있으며, 소정의 수수료를 받을 수 있습니다.
자주 묻는 질문 (FAQ)
Q1. TypeScript 5는 TypeScript 4.x와 호환되나요? A. 대부분 하위 호환됩니다. 단, 실험적 데코레이터를 사용 중이라면 experimentalDecorators: true 설정 유지가 필요합니다.
Q2. satisfies와 as 단언의 차이는? A. as는 타입 검증 없이 강제 변환합니다. satisfies는 타입 제약을 검증하면서도 원래 리터럴 추론을 유지합니다.
Q3. 표준 데코레이터로 NestJS를 사용할 수 있나요? A. NestJS는 현재 experimentalDecorators: true 기반입니다. 표준 데코레이터로의 전환은 NestJS 팀이 장기 로드맵으로 계획 중입니다.
Q4. const 타입 파라미터는 언제 사용하면 좋나요? A. 함수가 리터럴 값을 그대로 반환하거나 튜플/배열의 정확한 타입이 필요할 때 유용합니다. 라우팅, 설정 객체, 번역 키 등에 적합합니다.
Q5. using 키워드를 지원하는 Node.js 버전은? A. using은 TypeScript 5.2+와 Node.js 18+ 조합에서 사용 가능합니다. 타입스크립트가 Symbol.dispose를 활용하므로 폴리필이 필요할 수 있습니다.
Q6. NoInfer는 실무에서 어떤 경우에 쓰나요? A. API 함수의 타입 파라미터가 기본값에 의해 의도치 않게 넓어지는 것을 방지할 때 유용합니다. 특히 제네릭 팩토리 함수 작성 시 활용됩니다.
Q7. TypeScript 5.4에서 클로저 타입 좁히기란? A. 기존에는 함수 내부 클로저에서 타입 좁히기가 유지되지 않는 경우가 있었습니다. 5.4부터 클로저 내에서도 마지막 할당이 타입 좁히기에 사용됩니다.
Q8. verbatimModuleSyntax 옵션이 무엇인가요? A. 타입 전용 import/export를 import type과 export type으로 명시적으로 구분하도록 강제합니다. 번들러와의 호환성을 높이고 불필요한 런타임 코드를 제거합니다.
🔧 이 글과 관련된 무료 도구
이 글과 관련된 상품 (TypeScript5)[광고/제휴]
이 포스팅은 쿠팡 파트너스, 아마존 어소시에이트, 알리익스프레스 제휴 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다. 이는 상품 가격에 영향을 주지 않습니다.
As an Amazon Associate, Coupang Partner, and AliExpress affiliate, I earn from qualifying purchases at no extra cost to you.
관련 글
2026년 블로그 SEO 완벽 가이드. 구글 E-E-A-T·AI Overview·코어 업데이트 대응 전략. 롱테일 키워드·FAQ 구조·테크니컬 ...
IT2026 AI 코딩 도구 비교 — GitHub Copilot vs Claude vs Cursor 실사용 후기GitHub Copilot, Claude Code, Cursor를 6개월 이상 실사용한 후기 및 2026년 최신 기능 기준 성능·가격·생산성 완...
IT노션 업무 자동화 완벽 가이드 — API 연동부터 템플릿까지ITVPN 추천 2026 — 속도·보안·가격 비교 TOP 5 (실측 데이터)