TypeScript 54~58: 2026년에 실제로 중요한 기능들
출처: Dev.to
TypeScript 5.4부터 5.8까지: 2026년에 실제로 중요한 기능들
저는 3년 동안 TypeScript 릴리스를 추적해 왔습니다. 대부분의 릴리스 노트는 잡음에 불과합니다— “특정 코너 케이스에서 추론이 개선되었습니다” 라고 해도 실제 코딩 방식에는 큰 변화가 없습니다. 여기서는 지난 6번의 TypeScript 버전 중 실제로 내 일상 워크플로에 도입한 기능들을 실용적인 예시와 함께 정리합니다.
공개: 이 글에는 제휴 링크가 포함되어 있습니다. 위 링크를 통해 가입하면 추가 비용 없이 커미션을 받을 수 있습니다.
Template Literal Types에서 infer (드디어 고정)
템플릿 리터럴 타입은 문자열 타입의 일부를 추출하기 위해 infer를 도입했습니다. 하지만 초기 구현은 버그가 있었고—특정 상황에서 never를 추론했습니다.
// 5.8 이전: 문제 발생
type ExtractRoute = T extends `${infer Method} ${infer Path}`
? { method: Method; path: Path }
: never;
// 5.8에서는 이제 안정적으로 동작합니다
type Route = ExtractRoute;
// { method: "GET"; path: "/api/users" }
실용 예시: 타입 안전한 API 라우터
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type RouteConfig = `${Method} ${string}`;
function registerRoute(route: T, handler: () => void) {
const [method, path] = route.split(' ') as [Method, string];
console.log(`Registering ${method} ${path}`);
}
registerRoute('GET /users', () => {});
registerRoute('POST /users', () => {});
// registerRoute('PATCH /users', () => {}); // 오류: 'PATCH'는 Method에 할당할 수 없음
선택적 엄격 검사 활성화
{
"compilerOptions": {
"strictNullChecks": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
실질적인 영향
점진적으로 엄격성을 도입할 수 있습니다. 가장 가치가 높은 strictNullChecks부터 시작해, 문제가 해결되면 다른 플래그를 차례로 추가하세요.
// 이전: 분배 조건 타입을 사용해야 함
type MaybeArray = T extends any ? T[] : never;
// 이제: 기본값을 활용해 더 깔끔하게
type MaybeArray = T extends any ? T[] : Fallback;
// 사용 예:
type A = MaybeArray; // string[]
type B = MaybeArray; // string[] (여기서는 변함 없지만 메커니즘이 작동함)
5.6에서 도입된 Iterator 헬퍼
배열 메서드를 흉내낸 iterator 메서드가 추가되었습니다.
// 이전: 배열로 변환해야 .map(), .filter() 사용 가능
function* generateNumbers() {
yield* [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
}
const result = Array.from(generateNumbers())
.filter(n => n % 2 === 0)
.map(n => n * 2);
// 이제: 중간 배열 없이 iterator 메서드 직접 사용
const result = generateNumbers()
.filter(n => n % 2 === 0)
.map(n => n * 2)
.toArray();
// async generator도 동일하게 동작
async function* fetchPages(url: string) {
let page = 0;
while (page res.ok)
.take(5)
.map(res => res.json())
.toArray();
Enum 값에 대한 엄격 검사
enum Status {
Active = 'active',
Inactive = 'inactive'
}
// 5.6+부터 enum 값을 사용할 때 엄격히 체크됩니다
function processStatus(status: Status) {
// ...
}
processStatus(Status.Active); // ✅
processStatus('active'); // ❌ — 명시적 캐스팅 없이 오류 발생
이는 호환성을 깨는 변화이지만, 문자열이 실수로 넘어가는 버그를 한 번에 잡아줍니다.
최근 릴리스 중 가장 큰 품질 향상
5.5 이전: array.filter() 결과를 좁히지 못함
function isString(value: unknown): boolean {
return typeof value === 'string';
}
const mixed: (string | number)[] = ['hello', 42, 'world', 100];
// TypeScript가 좁히지 못해 결과 타입이 (string | number)[]
const strings = mixed.filter(isString);
5.5 이후: 타입 가드 함수가 타입 술어를 추론
// 이제 TypeScript가 타입 술어를 이해합니다
// strings는 올바르게 string[] 로 추론됩니다
정규식 리터럴 검증
const emailRegex = /^[a-z]+@[a-z]+\.[a-z]{2,}$/;
// 이전: \d+ 같은 흔한 오타도 오류 없이 통과
// 이제: 흔한 실수가 컴파일 타임에 잡힙니다
할당 후 좁히기 추적 개선 (5.4)
let value: string | number;
if (Math.random() > 0.5) {
value = 'hello';
} else {
value = 42;
}
// 5.4 이전: 할당 후 좁힌 정보를 잃어버림
// 함수 안에서만 좁힌 타입을 유지하려면 별도 함수가 필요
function getValue() {
let v: string | number;
if (Math.random() > 0.5) v = 'hello';
else v = 42;
return v;
}
// 5.4 이후: 클로저를 통해 좁힌 정보를 올바르게 추적
value.toString(); // ❌ 5.4+에서는 오류, toString이 양쪽에 존재해 모호함
function logValue(v: string | number) {
// 함수 내부에서 v가 좁혀짐
if (typeof v === 'string') {
console.log(v.toUpperCase());
} else {
console.log(v.toFixed(2));
}
}
NoInfer 도입 (5.4)
function createSignal(value: T, defaultValue: T): T {
return value ?? defaultValue;
}
// 이전: defaultValue에서도 T를 추론해 혼란 발생
// createSignal('hello', 42) 같은 호출이 오류를 일으킴
// 이제: NoInfer 로 추론 방향을 제어
function createSignal(value: NoInfer, defaultValue: T): T {
return value ?? defaultValue;
}
전체 릴리스 중 가장 큰 영향을 준 5가지 변화
-
.filter(isString)패턴만으로도 명시적 캐스팅 작업을 몇 시간씩 절감합니다. -
Array.from()변환이 수십 번 사라졌습니다. 제너레이터 파이프라인이 드디어 가독성을 얻었습니다. -
noUncheckedIndexedAccess(엄격 플래그)arr[0]접근 시 길이를 체크하지 않으면undefined가능성을 경고해 실제 버그를 잡아줍니다.
// "noUncheckedIndexedAccess": true 설정 시 const items = ['a', 'b', 'c']; const first = items[0]; // type: string | undefined if (first !== undefined) { console.log(first.toUpperCase()); // ✅ TypeScript가 string임을 인식 } -
GET ${string}패턴을 이용한 라우트 타입 지정이 5.8에서 프로덕션 수준으로 안정화되었습니다.type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; type Route = `${HttpMethod} /${string}`; const routes: Route[] = [ 'GET /users', // ✅ 'POST /users', // ✅ 'DELETE /users/1', // ✅ 'PATCH /users', // ❌ ]; -
타입 안전 API 클라이언트 구축 예시
type ApiRoute = { method: HttpMethod; path: string; response: unknown; }; function createApiClient(routes: ApiRoute[]) { return { async request( method: M, path: P, ...args: ExtractRoute extends { params: infer Params } ? [Params] : [] ) { // 타입 안전한 요청 구현 } }; }
마이그레이션 가이드
npm install typescript@latest
{
"compilerOptions": {
"strictNullChecks": true
}
}
모든 오류를 고친 뒤:
{
"compilerOptions": {
"noUncheckedIndexedAccess": true
}
}
isString 함수 예시
// 이전
function isString(value: unknown): boolean {
return typeof value === 'string';
}
// 이후 (동일 코드이지만 이제 TypeScript가 술어를 추론)
function isString(value: unknown): value is string {
return typeof value === 'string';
}
제너레이터 파이프라인 예시
// 이전
const doubled = Array.from(generator()).map(x => x * 2);
// 이후
const doubled = generator().map(x => x * 2).toArray();
결론
Type