TypeScript 5.5 — 실제 프로덕션 코드에 중요한 기능들

발행: (2026년 5월 24일 AM 05:22 GMT+9)
8 분 소요
원문: Dev.to

출처: Dev.to

TypeScript 5.5: 실제 프로덕션 코드에 중요한 기능들

TypeScript 5.5는 눈에 띄는 대형 기능과 미묘한 개선이 뒤섞여 대규모 코드베이스에서 크게 누적됩니다. 몇 달간 프로덕션에 적용해 본 결과, 우리 팀에 실제로 변화를 가져온 기능들을 정리했습니다.

자동 타입 프레디케이트 추론

작은 변화처럼 보이지만 개발자 경험(DX)이 크게 향상됩니다. 이제 TypeScript가 함수 구현부만 보고 타입 프레디케이트를 자동으로 추론합니다.

// 이전: TypeScript가 타입을 좁히지 못함
function isString(value: unknown): boolean {
  return typeof value === 'string';
}

const values: (string | number)[] = ['hello', 42, 'world', 100];

// 이렇게 해야 했음:
const strings = values.filter((v) => {
  if (isString(v)) {
    return v; // TypeScript는 여전히 v가 number일 수도 있다고 생각함!
  }
  return false;
});

// 혹은 더 명시적이지만 장황한 방법:
const strings = values.filter((v): v is string => isString(v));
// 이제 TypeScript가 타입 프레디케이트를 자동으로 추론함
function isString(value: unknown): boolean {
  return typeof value === 'string';
}

const values: (string | number)[] = ['hello', 42, 'world', 100];

// filter 콜백 내부에서 올바르게 타입이 좁혀짐
const strings = values.filter((v) => isString(v));
// strings는 정확히 string[] 타입
// 더 복잡한 프레디케이트도 동작
function isNonNullable<T>(value: T): value is NonNullable<T> {
  return value !== null && value !== undefined;
}

const mixed: (string | null | undefined)[] = ['a', null, 'b', undefined, 'c'];
const defined = mixed.filter(isNonNullable);
// defined는 string[] 타입
// 5.5 이전에는 모든 곳에 명시적인 타입 프레디케이트가 필요했음
interface User {
  id: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
}

function isAdmin(user: User): boolean {
  return user.role === 'admin';
}

function isActive(user: User): boolean {
  return user.email.includes('@'); // 단순화된 예시
}

const users: User[] = /* API 로부터 받아옴 */;

// 기존 방식: 명시적인 타입 프레디케이트
const admins = users.filter((u): u is User => isAdmin(u) && isActive(u));

// 5.5 방식: 프레디케이트가 자연스럽게 추론됨
const admins = users.filter((u) => isAdmin(u) && isActive(u));

정규식 타입 검사

정규식에 대한 타입 검사가 추가되어 실제 버그를 잡아냅니다.

// 이전에는 런타임 에러가 발생했음
const emailRegex = new RegExp('[a-z+', 'i'); // 정규식 구문 오류!

// TypeScript 5.5: 컴파일 타임에 오류 감지
// Error: Invalid regular expression: Range out of order in character class
// 또 다른 예시
const dateRegex = new RegExp('(\\d{4})-(\\d{2})-(\\d{2}', 'g'); // 닫는 괄호 누락
// Error: Invalid regular expression: Unterminated group
// 실수로 잘못된 문자를 이스케이프한 경우
const phoneRegex = new RegExp('\\d{3}[-*]\\d{3}[-*]\\d{4}');
// 정상! 하지만 의도한 것이:
// const zipRegex = new RegExp('\d{5}'); // 백슬래시 누락!
// TypeScript 5.5가 잡아냄: 문자열 리터럴에서 '\d'는 '\\d'이어야 함
// Error: Invalid escape sequence in string literal

필터링 시 출력 타입 자동 추론

타입 프레디케이트로 배열을 필터링할 때, 이제 TypeScript가 출력 타입을 올바르게 추론합니다.

interface ApiResponse {
  status: 'success' | 'error';
  data?: object;
  error?: string;
}

const responses: ApiResponse[] = await fetchAll();

// 5.5 이전: 필터 결과를 직접 타입 지정해야 함
const successful: ApiResponse[] = responses.filter((r) => r.status === 'success');
// 복잡한 상황에서는 번거로웠음

// 5.5에서는 차별화된 유니온을 더 잘 다룸
const successes = responses.filter((r) => r.status === 'success');
// successes는 (ApiResponse & { status: 'success' })[] 타입이며
// r.data는 이제 `object` 로 정확히 추론됨 (object | undefined 가 아님)

import assertions → import with 구문

// 5.5 이전: 이상한 edge case
import data from './data.json' assert { type: 'json' };
// 5.5 이후: 새로운 표준인 import attributes 구문
import data from './data.json' with { type: 'json' };

assert는 곧 with로 대체될 예정이며, 5.5는 미래에 대비한 코드를 작성하도록 돕습니다.

빌드 속도 개선

우리 코드베이스(~800k 라인, 300 패키지)에서 측정한 결과:

버전전체 빌드 시간증분 빌드 시간
TypeScript 5.445.2 s12.1 s
TypeScript 5.538.7 s9.8 s
  • 전체 빌드: 약 14 % 개선
  • 증분 빌드: 약 19 % 개선

속도 향상의 주요 원인:

  • 더 효율적인 타입 좁히기
  • 스마트한 stale 파일 감지
  • 해결된 import 캐시 최적화

satisfies 동작 개선

// 5.5에서 union 타입에 대한 `satisfies` 의미가 정교해짐
type Color = 'red' | 'green' | 'blue';
type Theme = Record<string, string>;

const theme = {
  primary: 'red',
  secondary: 'blue',
  custom: '#3498db', // 문자열이지만 Color 은 아님
} satisfies Theme;

// 이제 각 프로퍼티가 정확히 좁혀짐
const primary: Color = theme.primary; // 'red' (Color | string 아님)
const custom: string = theme.custom; // '#3498db' (Color | string 아님)

5.5 이전에는 satisfies만으로는 캐스팅이 필요했을 때가 있었습니다.

런타임 타입 지정은 여전히 필요

// 이 부분은 여전히 명시적인 타입 지정이 필요함 – TypeScript가 런타임을 추론할 수 없음
async function fetchUser(id: string): Promise<User> {
  // User 타입을 알려줘야 함
  const response = await fetch(`/api/users/${id}`);
  return response.json(); // 여전히 any, 타입 어설션 또는 스키마 검증 필요
}

// 권장 방법: 스키마 검증기 사용 (zod, typebox 등)
import { z } from 'zod';

const UserSchema = z.object({
  id: z.string(),
  email: z.string().email(),
  role: z.enum(['admin', 'user', 'guest']),
});

const response = await fetch(`/api/users/${id}`);
const user = UserSchema.parse(await response.json()); // 정확히 타입 지정됨

복잡한 추론은 아직 한계가 있음

function createReducer<S, A extends { type: string }>(
  initialState: S,
  handlers: Record<string, (state: S, action: A) => S>
): (state: S | undefined, action: A) => S {
  return (state = initialState, action) => {
    const handler = handlers[action.type];
    return handler ? handler(state, action) : state;
  };
}

// 일부 시나리오에서는 여전히 명시적인 타입이 필요함
// 이는 TypeScript 타입 시스템의 근본적인 특성

업그레이드 방법

npm install typescript@5.5 --save-dev
# 또는
pnpm add -D typescript@5.5

마이그레이션 체크리스트

  • filter 콜백에서 value is 패턴 검색

    // 기존: 불필요하게 명시적
    array.filter((item): item is Type => predicate(item))
    // 이제:
    array.filter(predicate)
  • import assertions 교체

    // 기존
    import foo from './foo.json' assert { type: 'json' };
    // 교체
    import foo from './foo.json' with { type: 'json' };
  • 정규식 오류를 조기에 잡는 lint 규칙 추가

    // ESLint 규칙: no-invalid-regexp
  • CI에 커스텀 정규식 검증 스크립트 추가

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

프로젝트를 위한 AI 지시문을 만들고, 설치하고, 관리하세요 — 코딩이 필요 없습니다. CREATE 이름을 정하고, 카테고리를 선택하고, 원하는 것을 설명하세요 — 마법사가 자동으로 구성합니다.