본문 바로가기
Language/Typescript

타입챌린지 : 1978-Percentage Parser (medium)

by hsloth 2023. 4. 27.

 

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.

 

https://github.com/type-challenges/type-challenges/blob/main/questions/01978-medium-percentage-parser/README.md

 

GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

Collection of TypeScript type challenges with online judge - GitHub - type-challenges/type-challenges: Collection of TypeScript type challenges with online judge

github.com

 

설명에는 정규표현식 어쩌구 저쩌구 적혀있는데... 굳이 읽을 필요는 없는 것 같다.

제네릭인자를 [- or +, number, percentage] 로 분해하여 리턴하는 타입이다.

 

일단, 나의 첫번째 답을 봐보자.

// 정답이긴하나... 노가다이다.
type PercentageParser<A extends string> = A extends '%' ? ['', '', '%'] : A extends `${infer F}${infer O}%` ?
  F extends '+' | '-' ? [F, O, '%'] : ['', `${F}${O}`, '%'] :
  A extends `${infer F}${infer O}` ?
  F extends '+' | '-' ? [F, O, ''] : ['', A, ''] :
  ['', '', '']

정답이긴하나... 노가다를 통해서 정의를 하였기 때문에 보기가 힘들고 이해하기 어렵다.

 

두번째 답은 재귀를 이용해서 풀어보았다.

type PercentageParser<A extends string, X extends string='', Y extends string=''> = A extends `${infer F}${infer O}` ?
  F extends '+' | '-' ? PercentageParser<O, F> : 
  F extends '%' ? [X, Y, '%'] : PercentageParser<O, X, `${Y}${F}`> :
  [X, Y, '']
  
  
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type Case0 = ['', '', '']
type Case1 = ['+', '', '']
type Case2 = ['+', '1', '']
type Case3 = ['+', '100', '']
type Case4 = ['+', '100', '%']
type Case5 = ['', '100', '%']
type Case6 = ['-', '100', '%']
type Case7 = ['-', '100', '']
type Case8 = ['-', '1', '']
type Case9 = ['', '', '%']
type Case10 = ['', '1', '']
type Case11 = ['', '100', '']

type cases = [
  Expect<Equal<PercentageParser<''>, Case0>>,
  Expect<Equal<PercentageParser<'+'>, Case1>>,
  Expect<Equal<PercentageParser<'+1'>, Case2>>,
  Expect<Equal<PercentageParser<'+100'>, Case3>>,
  Expect<Equal<PercentageParser<'+100%'>, Case4>>,
  Expect<Equal<PercentageParser<'100%'>, Case5>>,
  Expect<Equal<PercentageParser<'-100%'>, Case6>>,
  Expect<Equal<PercentageParser<'-100'>, Case7>>,
  Expect<Equal<PercentageParser<'-1'>, Case8>>,
  Expect<Equal<PercentageParser<'%'>, Case9>>,
  Expect<Equal<PercentageParser<'1'>, Case10>>,
  Expect<Equal<PercentageParser<'100'>, Case11>>,
]

배열에 들어 갈 값들을 제네릭 X와 Y에 저장해서 재귀로 넘기는 방식이다.

A extends `${infer F}${infer O}` ? : A가 첫번째 문자 F와 나머지 문자열 O로 나뉠 때,

F extends '+' | '-' ? PercentagerParfser<O, F> : F가 +나-라면, X에 부호를 저장해서 나머지 O를 넘기면서 재귀를 호출하고,

F extends '%' ? [X, Y, '%'] : PercentageParser<O, X, `${Y}${F}`> : F가 부호가 아닐 때, F가 %라면 [X, Y, '%']를 리턴하고 아니라면 현재값을 Y에 담아서 재귀를 호출한다.

[X, Y, ''] : 재귀가 마지막까지 호출되었다면, [X, Y, ''] 를 호출한다.

 

 

다른 분들의 답을 봐보자.

type CheckPrefix<T> = T extends '+' | '-' ? T : never;
type CheckSuffix<T> =  T extends `${infer P}%` ? [P, '%'] : [T, ''];
type PercentageParser<A extends string> = A extends `${CheckPrefix<infer L>}${infer R}` ?
	[L, ...CheckSuffix<R>] : ['', ...CheckSuffix<A>];

와~ 진짜 천재인거같다.

문자열을 분산 조건부 타입으로 나눌 때, `${CheckPrefix<infer L>}${infer R}` 과 같이 나눌 수 있다는 걸 처음 알게되었다.

이렇게 나누면 자동적으로 L에 첫 문자가 들어가게 된다... 크.. 오늘도 역시 한수 배우고 간다.