-
[Type-challenges] 난이도 Medium - (5)Front-End(Web)/Typescript 2023. 2. 23. 02:18반응형
Github 챌린지 문제들을 풀고 관련된 내용을 정리하면서, 부족했던 타입스크립트 기본지식을 다지고자 한다. (주 1-2회)
https://github.com/type-challenges/type-challenges
📘 목차 - Medium
- Percentage Parser
- Drop Char
- MinusOne
- PickByType
- StartsWith
- EndsWith
- PartialByKeys
📘 문제 및 풀이
1. Percentage Parser
Implement PercentageParser. According to the /^(\+|\-)?(\d*)?(\%)?$/ regularity to match T and get three matches.
The structure should be: [plus or minus, number, unit] If it is not captured, the default is an empty string.
For example:type PString1 = '' type PString2 = '+85%' type PString3 = '-85%' type PString4 = '85%' type PString5 = '85' type R1 = PercentageParser<PString1> // expected ['', '', ''] type R2 = PercentageParser<PString2> // expected ["+", "85", "%"] type R3 = PercentageParser<PString3> // expected ["-", "85", "%"] type R4 = PercentageParser<PString4> // expected ["", "85", "%"] type R5 = PercentageParser<PString5> // expected ["", "85", ""]
/* _____________ 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>>, ]
🖌 풀이
// your answers type PercentageParser<T> = T extends `${infer F}${infer R}` ? F extends "+" | "-" ? [F, ...(R extends `${infer X}%` ? [X, "%"] : [R, ""])] : ["", ...(T extends `${infer Y}%` ? [Y, "%"] : [T, ""])] : ["", "", ""];
위 풀이가 생각보다 심플한 해결법을 주었다.
먼저, 첫 글자인 F가 +/-에 해당하면 이를, 아니면 빈 문자열을 시작으로 배열을 반환한다.
이후, 전자면 나머지 R을, 후자면 전체 T를 단언타입과 %에 해당하는지 여부에 따라 분기해주면 된다.
모든 경우에 해당하지 않으면, 빈 문자열들로 이루어진 배열을 반환한다.
2. Drop Char
Drop a specified char from a string.
For example:type Butterfly = DropChar<' b u t t e r f l y ! ', ' '> // 'butterfly!'
/* _____________ Test Cases _____________ */ 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 ! '>>, ]
🖌 풀이
type DropChar<S, C> = S extends `${infer F}${infer R}` ? F extends C ? DropChar<R, C> : `${F}${DropChar<R, C>}` : S
이전에 계속 풀었던 String 타입을 재귀적으로 푸는 문제이다. 다만, 앞에 F를 붙이면서 안쪽에서 DropChar로 재귀하고 있는 점이 다르다.
3. MinusOne
Given a number (always positive) as a type. Your type should return the number decreased by one.
For example:type Zero = MinusOne<1> // 0 type FiftyFour = MinusOne<55> // 54
/* _____________ Test Cases _____________ */ 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<0>, -1>>, Expect<Equal<MinusOne<9_007_199_254_740_992>, 9_007_199_254_740_991>>, ]
🖌 풀이
풀이는 이 링크를 참고하자. 숫자를 뒤집어서 0이 나오면 이를 최대한 줄이고, 그것이 아니면 이것의 -1 한 숫자를 반환한다.
0~9까지 숫자에 맵핑되는 -1 숫자를 9~0으로 다시 만든다. (다소 노가다성의 풀이다)
4. PickByType
From T, pick a set of properties whose type are assignable to U.
For Exampletype OnlyBoolean = PickByType<{ name: string count: number isReadonly: boolean isEnable: boolean }, boolean> // { isReadonly: boolean; isEnable: boolean; }
/* _____________ Test Cases _____________ */ 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 }>>, ]
🖌 풀이
type PickByType<T, U> = { [P in keyof T as T[P] extends U ? P : never] : T[P]; }
as에서 키 뿐만이 아니라 위처럼 값을 가지고도 분기를 할 수 있다. T[P]가 U에 해당하는 경우가 아니면 키값을 never로 지정한다.
5. StartsWith
Implement StartsWith<T, U> which takes two exact string types and returns whether T starts with U
For exampletype a = StartsWith<'abc', 'ac'> // expected to be false type b = StartsWith<'abc', 'ab'> // expected to be true type c = StartsWith<'abc', 'abcd'> // expected to be false
/* _____________ Test Cases _____________ */ 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>>, ]
🖌 풀이
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false;
재귀로 풀지 않아도 쉽게 풀 수 있는 문제였다. T가 U와 string을 합친 리터럴에 해당하는지 여부를 반환한다.
6. EndsWith
Implement EndsWith<T, U> which takes two exact string types and returns whether T ends with U
For example:type a = EndsWith<'abc', 'bc'> // expected to be true type b = EndsWith<'abc', 'abc'> // expected to be true type c = EndsWith<'abc', 'd'> // expected to be false
/* _____________ Test Cases _____________ */ 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>>, ]
🖌 풀이
type EndsWith<T extends string, U extends string> = T extends `${string}${U}` ? true : false
위의 StartsWith 문제를 반대로 풀어주면 됬다.
7. PartialByKeys
Implement a generic PartialByKeys<T, K> which takes two type argument T and K.
K specify the set of properties of T that should set to be optional. When K is not provided, it should make all properties optional just like the normal Partial<T>.
For exampleinterface User { name: string age: number address: string } type UserPartialName = PartialByKeys<User, 'name'> // { name?:string; age:number; address:string }
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface User { name: string age: number address: string } interface UserPartialName { name?: string age: number address: string } interface UserPartialNameAndAge { name?: string age?: number address: string } type cases = [ Expect<Equal<PartialByKeys<User, 'name'>, UserPartialName>>, Expect<Equal<PartialByKeys<User, 'name' | 'age'>, UserPartialNameAndAge>>, Expect<Equal<PartialByKeys<User>, Partial<User>>>, // @ts-expect-error Expect<Equal<PartialByKeys<User, 'name' | 'unknown'>, UserPartialName>>, ]
🖌 풀이
type Merge<T> = { [K in keyof T]: T[K]; } type PartialByKeys<T, K extends keyof T = keyof T> = Merge<{ [P in keyof T as P extends K ? P : never]?: T[P]; } & { [P in keyof T as P extends K ? never : P]: T[P]; }>;
풀이는 예상한대로 였지만, 마지막에 두 타입을 Singular Type으로 묶어주기 위해선 Merge 과정이 필요하다.
반응형'Front-End(Web) > Typescript' 카테고리의 다른 글
[Type-challenges] 난이도 Medium - (7) (0) 2023.02.27 [Type-challenges] 난이도 Medium - (6) (0) 2023.02.27 [Type-challenges] 난이도 Medium - (4) (0) 2023.02.16 [Type-challenges] 난이도 Medium - (3) (0) 2023.02.14 [Type-challenges] 난이도 Medium - (2) (0) 2023.02.14