1978 - Percentage Parser
문제: PercentageParser을 구현하세요.
/^(\\+|\\-)?(\\d*)?(\\%)?$/
정규식에 따라 T를 일치시키고 3개의 일치 요소를 얻습니다. /* _____________ 여기에 코드 입력 _____________ */ type UnsignedPercentageParser<A extends string> = A extends `${infer Num}%` ? [Num, "%"] : [A, ""]; type PercentageParser<A extends string> = A extends `${infer Sign extends "+" | "-"}${infer Rest}` ? [Sign,...UnsignedPercentageParser<Rest>] : ["", ...UnsignedPercentageParser<A>] /* _____________ 테스트 케이스 _____________ */ 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>>, ]
- 풀이
- 한번에 하기 파싱하기 어려워 나누어 해결
2070 - Drop Char
문제: Drop a specified char from a string.
/* _____________ 여기에 코드 입력 _____________ */ type DropChar<S, C> = S extends `${infer First}${infer Rest}` ? First extends C ? DropChar<Rest, C> : `${First}${DropChar<Rest, C>}` : S; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ // @ts-expect-error Expect<Equal<DropChar<'butter fly!', ''>, 'butterfly!'>>, Expect<Equal<DropChar<'butter fly!', ' '>, 'butterfly!'>>, Expect<Equal<DropChar<'butter fly!', '!'>, 'butter fly'>>, Expect<Equal<DropChar<' butter fly! ', ' '>, 'butterfly!'>>, Expect<Equal<DropChar<' b u t t e r f l y ! ', ' '>, 'butterfly!'>>, Expect<Equal<DropChar<' b u t t e r f l y ! ', 'b'>, ' u t t e r f l y ! '>>, Expect<Equal<DropChar<' b u t t e r f l y ! ', 't'>, ' b u e r f l y ! '>>, ]
- 풀이
- 문자열 순차적으로 순회하며 C와 조건부 타입 연산
- true 면 제외 후 연산
- false 면 포함
2257 - MinusOne
문제: Given a number (always positive) as a type. Your type should return the number decreased by one.
/* _____________ 여기에 코드 입력 _____________ */ // 숫자 빼기 맵퍼 type RotateDigitString<T extends string> = T extends "0" ? "9" : T extends "1" ? "0" : T extends "2" ? "1" : T extends "3" ? "2" : T extends "4" ? "3" : T extends "5" ? "4" : T extends "6" ? "5" : T extends "7" ? "6" : T extends "8" ? "7" : T extends "9" ? "8" : never; type RotateDigitStringTest = RotateDigitString<"0">; // "9" // 전체를 변경해야함 type StringToNumber<T extends string> = T extends `${infer Num extends number}` ? Num : never; type StringToNumberTest1 = StringToNumber<"00"> // number type StringToNumberTest2 = StringToNumber<"1"> // 1 type StringToNumberTest3 = StringToNumber<"001"> // number type SplitString<T extends string, C extends string[] = []> = T extends `${infer First}${infer Rest}` ? SplitString<Rest, [...C, First]> : C; type SplitStringTest1 = SplitString<"1000"> // ["1", "0", "0", "0"] type SplitStringTest2 = SplitString<"1"> // ["1"] type SubstractOne< T extends string[], Borrow extends boolean = true, C extends string[] = [] > = T extends [...infer Rest extends string[], infer Last extends string] ? Borrow extends true ? Last extends "0" ? SubstractOne<Rest, true, [RotateDigitString<Last>, ...C]> : SubstractOne<Rest, false, [RotateDigitString<Last>, ...C]> : SubstractOne<Rest, false, [Last, ...C]> : C; type SubstractOneTest1 = SubstractOne<["1", "1"]> // ["1", "0"] type SubstractOneTest2 = SubstractOne<["5", "5"]> // ["5", "4"] type SubstringOneTest3 = SubstractOne<["1", "0", "0"]> // ["0", "9", "9"] type SubstractOneTest4 = SubstractOne<["1"]> // ["0"] type JoinString<T extends string[], C extends string = ""> = T extends [infer First extends string, ...infer Rest extends string[]] ? JoinString<Rest,`${C}${First}`> : C; type JoinStringTest1 = JoinString<["9", "8", "0"]> // "089" type JoinStringTest2 = JoinString<["1"]> // "1" type RemoveZeroPad<T extends string> = T extends `0${infer Rest}` ? Rest extends "" ? "0" : RemoveZeroPad<Rest> : T; type RemoveZeroPadTest1 = RemoveZeroPad<"0101"> // "101" type RemoveZeroPadTest2 = RemoveZeroPad<"0"> // "0" type MinusOne<T extends number> = StringToNumber<RemoveZeroPad<JoinString<SubstractOne<SplitString<`${T}`>>>>> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<MinusOne<1>, 0>>, Expect<Equal<MinusOne<55>, 54>>, Expect<Equal<MinusOne<3>, 2>>, Expect<Equal<MinusOne<100>, 99>>, Expect<Equal<MinusOne<1101>, 1100>>, Expect<Equal<MinusOne<9_007_199_254_740_992>, 9_007_199_254_740_991>>, ]
- 배운 점
- StringToNumber
// 전체를 변경해야함 type StringToNumber<T extends string> = T extends `${infer Num extends number}` ? Num : never; type StringToNumberTest1 = StringToNumber<"00"> // number type StringToNumberTest2 = StringToNumber<"1"> // 1 type StringToNumberTest3 = StringToNumber<"001"> // number type StringToNumberTest4 = StringToNumber<"-1"> // -1 type StringToNumberTest5 = StringToNumber<"0.5"> // 0.5
00 과 같이 적절한 숫자가 아닌 경우 타입을 number 로, 아니면 숫자 리터럴을 return
- 풀이
- 여러 과정을 추가해야 풀린다는 사실을 확인 후 풀이
- RotateDigitString 을 통해 숫자 1 을 빼는 과정 매핑
- string 형태로 되어있는 것을 number 로 변경해주는 것이 필요 (마지막에 숫자 리터럴 타입으로 리턴해야하기 때문)
- SplitString 을 통해 각 문자열 리터럴을 쪼개서 배열 형태로 (문자열 리터럴 그대로 사용하면 앞에서 밖에 읽지 못하기 때문)
- SubstractOne 을 통해 배열로 변환된 숫자 배열에서 1을 뺌, Borrow 변수를 통해 0 빼기 여부 체크
- JoinString 을 통해 Split 한 문자열 합침
- RemoveZeroPad 를 통해 앞의 0 문자 제거
2595 - PickByType
문제: From
T
, pick a set of properties whose type are assignable to U
./* _____________ 여기에 코드 입력 _____________ */ // type PickByType<T, U> = {[index in keyof T as keyof (T[index] extends U ? T[index] : never)]: T[index]}; type PickByType<T, U> = {[K in keyof T as (T[K] extends U ? K : never)]: T[K]}; type Test = PickByType<Model, boolean> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Model { name: string count: number isReadonly: boolean isEnable: boolean } type cases = [ Expect<Equal<PickByType<Model, boolean>, { isReadonly: boolean, isEnable: boolean }>>, Expect<Equal<PickByType<Model, string>, { name: string }>>, Expect<Equal<PickByType<Model, number>, { count: number }>>, ]
- 배운 점
- K 자체를 타입 연산의 결과로 사용할 수 있다.
2688 - StartsWith
문제: Implement
StartsWith<T, U>
which takes two exact string types and returns whether T
starts with U
/* _____________ 여기에 코드 입력 _____________ */ type StartsWith<T extends string, U extends string> = T extends `${U}${infer _}` ? true : false; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<StartsWith<'abc', 'ac'>, false>>, Expect<Equal<StartsWith<'abc', 'ab'>, true>>, Expect<Equal<StartsWith<'abc', 'abc'>, true>>, Expect<Equal<StartsWith<'abc', 'abcd'>, false>>, Expect<Equal<StartsWith<'abc', ''>, true>>, Expect<Equal<StartsWith<'abc', ' '>, false>>, Expect<Equal<StartsWith<'', ''>, true>>, ]
- 배운 점
- 타입스크립트 문자열 리터럴 타입을 조건부 연산이 매칭 할 때는 패턴 매칭처럼 동작하기 때문에 간단하게 해결 할 수 있다.
2693 - EndsWith
문제: Implement `EndsWith<T, U>` which takes two exact string types and returns whether `T` ends with `U`
/* _____________ 여기에 코드 입력 _____________ */ type EndsWith<T extends string, U extends string> = T extends `${infer _}${U}` ? true : false; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<EndsWith<'abc', 'bc'>, true>>, Expect<Equal<EndsWith<'abc', 'abc'>, true>>, Expect<Equal<EndsWith<'abc', 'd'>, false>>, Expect<Equal<EndsWith<'abc', 'ac'>, false>>, Expect<Equal<EndsWith<'abc', ''>, true>>, Expect<Equal<EndsWith<'abc', ' '>, false>>, ]