TypeScript 54~58: 2026년에 실제로 중요한 기능들

발행: (2026년 5월 23일 PM 10:34 GMT+9)
9 분 소요
원문: Dev.to

출처: 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가지 변화

  1. .filter(isString) 패턴만으로도 명시적 캐스팅 작업을 몇 시간씩 절감합니다.

  2. Array.from() 변환이 수십 번 사라졌습니다. 제너레이터 파이프라인이 드디어 가독성을 얻었습니다.

  3. 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임을 인식
    }
  4. 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',   // ❌
    ];
  5. 타입 안전 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

0 조회
Back to Blog

관련 글

더 보기 »

내 스킬

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