Typescript Infer 5편 : 문자열을 타입으로 파싱

프디·2025년 4월 13일

Typescript infer

목록 보기
5/5

문제 정의

type ParseObject<T extends string> = ???

type Result = ParseObject<'{ a: string; b: number; }'>;
// 기대 결과: { a: string } & { b: number }
  • 목표: 타입 수준에서 문자열을 해석해 객체 타입으로 바꾸는 파서(parser) 를 만드는 것

해결

1단계. 중괄호 제거 – ExtractInnerText<T>

우선 문자열에서 {} 안쪽만 추출하자.

type ExtractInnerText<T extends string> =
  T extends `{${infer Inner}}` ? Inner : never;

type T1 = ExtractInnerText<'{ a: string; b: number; }'>;
// " a: string; b: number; "
  • infer로 중괄호 안의 문자열을 추출하고, Trim을 이용해 앞뒤 공백을 제거한 값을 반환한다.
  • 이때 공백도 함께 들어오므로 나중에 trim 필요

2단계. 세미콜론 단위로 나누기 – SplitBySemicolon<T>

Trim

type Trim<T extends string> =
  T extends ` ${infer R}` ? Trim<R> :
  T extends `${infer R} ` ? Trim<R> :
  T;
Trim<'  hello  '>
→ Trim<' hello  '>      // 앞 공백 제거
→ Trim<'hello  '>       // 앞 공백 또 제거
→ Trim<'hello '>        // 뒤 공백 제거
→ Trim<'hello'>         // 더 이상 앞뒤 공백 없음
→ T 반환 → 'hello'
  • 앞뒤에 공백이 더 이상 없을 때 T를 그대로 반환한다.
    (즉, 모든 공백이 제거된 최종 문자열을 반환)
type SplitBySemicolon<T extends string> =
  T extends `${infer Head};${infer Tail}`
    ? [Trim<Head>, ...SplitBySemicolon<Tail>]
    : T extends '' ? [] : [Trim<T>];

type T2 = SplitBySemicolon<'a: string; b: number;'>;
// ['a: string', 'b: number']
  • 세미콜론으로 자르고
  • 앞뒤 공백 제거 (Trim)
  • 빈 문자열은 제거

3단계. 문자열 "a: string" → 객체 타입 { a: string } 으로 변환 – ParseEntry<T>

type ParseValue<T extends string> =
  T extends 'string' ? string :
  T extends 'number' ? number :
  T extends 'boolean' ? boolean :
  unknown;

type ParseEntry<T extends string> =
  T extends `${infer Key}:${infer Val}`
    ? { [K in Trim<Key>]: ParseValue<Trim<Val>> }
    : {};
  • a: stringastring으로 나누고
  • 타입명을 실제 타입으로 매핑
  • 공백 제거는 항상 중요
type T3 = ParseEntry<'a: string'>; // { a: string }
type T4 = ParseEntry<'b:number'>;  // { b: number }

4단계. 여러 개의 엔트리를 합치기 – UnionToIntersection<T>

type UnionToIntersection<U> =
  (U extends any ? (x: U) => any : never) extends (x: infer R) => any ? R : never;
type ParseObject<T extends string> =
  UnionToIntersection<
    ParseEntry<SplitBySemicolon<ExtractInnerText<T>>[number]>
  >;

작동 설명:

  • SplitBySemicolon<...>으로 얻은 타입은 튜플: ['a: string', 'b: number']
  • [number]으로 유니온으로 바꿔줌: 'a: string' | 'b: number'
  • 각각을 ParseEntry로 파싱하면: { a: string } | { b: number }
  • 마지막으로 UnionToIntersection을 통해 { a: string } & { b: number }로 결합

최종 결과

type ParseObject<T extends string> =
  UnionToIntersection<
    ParseEntry<SplitBySemicolon<ExtractInnerText<T>>[number]>
  >;

type Result = ParseObject<'{ a: string; b: number; }'>;
// ✅ { a: string } & { b: number }

마무리 요약

단계목적
ExtractInnerText<T>문자열에서 { ... } 안 내용만 추출
SplitBySemicolon<T>key: value 쌍으로 분리
ParseEntry<T>"a: string" 같은 쌍을 실제 객체로 변환
UnionToIntersection<T>각각의 객체를 하나로 병합 (&)
ParseObject<T>최종 완성된 타입 파서

profile
프론트엔드개발자인디

0개의 댓글