-
[Type-challenges] 난이도 Medium - (11)Front-End(Web)/Typescript 2023. 3. 3. 04:00반응형
Github 챌린지 문제들을 풀고 관련된 내용을 정리하면서, 부족했던 타입스크립트 기본지식을 다지고자 한다. (주 1-2회)
https://github.com/type-challenges/type-challenges
📘 목차 - Medium
- GetMiddleElement
- Integer
- ToPrimitive
- DeepMutable
- All
- Filter
📘 문제 및 풀이
1. GetMiddleElement
Get the middle element of the array by implementing a GetMiddleElement method, represented by an array
If the length of the array is odd, return the middle element If the length of the array is even, return the middle two elements
For exampletype simple1 = GetMiddleElement<[1, 2, 3, 4, 5]>, // expected to be [3] type simple2 = GetMiddleElement<[1, 2, 3, 4, 5, 6]> // expected to be [3, 4]
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<GetMiddleElement<[]>, []>>, Expect<Equal<GetMiddleElement<[1, 2, 3, 4, 5]>, [3]>>, Expect<Equal<GetMiddleElement<[1, 2, 3, 4, 5, 6]>, [3, 4]>>, Expect<Equal<GetMiddleElement<[() => string]>, [() => string]>>, Expect<Equal<GetMiddleElement<[() => number, '3', [3, 4], 5]>, ['3', [3, 4]]>>, Expect<Equal<GetMiddleElement<[() => string, () => number]>, [() => string, () => number]>>, Expect<Equal<GetMiddleElement<[never]>, [never]>>, ] // @ts-expect-error type error = GetMiddleElement<1, 2, 3>
🖌 풀이
type GetMiddleElement<T extends readonly unknown[]> = T['length'] extends 0 | 1 | 2 ? T : T extends [unknown, ...infer Middle, unknown] ? GetMiddleElement<Middle> : never
T의 길이가 0, 1, 2 중 하나면 중간요소가 맞으므로 이를 반환하면 된다.
아니라면, 양 옆을 unknown 요소로 제거한 가운데(Middle) 배열에 유틸리티를 재귀한다.
2. Integer
Please complete type Integer<T>, type T inherits from number, if T is an integer return it, otherwise return never.
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' import { ExpectFalse, NotEqual } from '@type-challenges/utils' let x = 1 let y = 1 as const type cases1 = [ Expect<Equal<Integer<1>, 1>>, Expect<Equal<Integer<1.1>, never>>, Expect<Equal<Integer<1.0>, 1>>, Expect<Equal<Integer<typeof x>, never>>, Expect<Equal<Integer<typeof y>, 1>>, ]
🖌 풀이
type Integer<T extends number> = number extends T ? never : `${T}` extends `${string}.${string}` ? never : T
아래의 삼항연산 코드로도 구분이 가능하다. T(숫자)를 문자화한 값에 소수점이 있는지 여부로 판단하는 것이다.
단, 케이스4 처럼 typeof의 경우를 필터링하기 위해 T에 number 타입으로 넘어왔는지 여부를 추가로 분기해준다.
3. ToPrimitive
Convert a property of type literal (label type) to a primitive type.
For exampletype X = { name: 'Tom', age: 30, married: false, addr: { home: '123456', phone: '13111111111' } } type Expected = { name: string, age: number, married: boolean, addr: { home: string, phone: string } } type Todo = ToPrimitive<X> // should be same as `Expected`
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type PersonInfo = { name: 'Tom' age: 30 married: false addr: { home: '123456' phone: '13111111111' } hobbies: ['sing', 'dance'] } type ExpectedResult = { name: string age: number married: boolean addr: { home: string phone: string } hobbies: [string, string] } type cases = [ Expect<Equal<ToPrimitive<PersonInfo>, ExpectedResult>>, ]
🖌 풀이
type ToPrimitive<T> = { [P in keyof T]: T[P] extends string ? string : T[P] extends number ? number : T[P] extends boolean ? boolean : ToPrimitive<T[P]>; };
원시타입(string, number, boolean, bigint, symbol) 이라면 그대로 반환해주고, 아니라면 유틸리티를 재귀해준다.
객체뿐만 아니라 배열도 [P in keyof T] 로 접근이 가능한 것이다.
4. DeepMutable
Implement a generic DeepMutable which make every parameter of an object - and its sub-objects recursively - mutable.
For example
type X = { readonly a: () => 1 readonly b: string readonly c: { readonly d: boolean readonly e: { readonly g: { readonly h: { readonly i: true readonly j: "s" } readonly k: "hello" } } } } type Expected = { a: () => 1 b: string c: { d: boolean e: { g: { h: { i: true j: "s" } k: "hello" } } } } type Todo = DeepMutable<X> // should be same as `Expected`
You can assume that we are only dealing with Objects in this challenge. Arrays, Functions, Classes and so on do not need to be taken into consideration. However, you can still challenge yourself by covering as many different cases as possible.
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Test1 { readonly title: string readonly description: string readonly completed: boolean readonly meta: { readonly author: string } } type Test2 = { readonly a: () => 1 readonly b: string readonly c: { readonly d: boolean readonly e: { readonly g: { readonly h: { readonly i: true readonly j: 's' } readonly k: 'hello' } readonly l: readonly [ 'hi', { readonly m: readonly ['hey'] }, ] } } } interface DeepMutableTest1 { title: string description: string completed: boolean meta: { author: string } } type DeepMutableTest2 = { a: () => 1 b: string c: { d: boolean e: { g: { h: { i: true j: 's' } k: 'hello' } l: [ 'hi', { m: ['hey'] }, ] } } } type cases = [ Expect<Equal<DeepMutable<Test1>, DeepMutableTest1>>, Expect<Equal<DeepMutable<Test2>, DeepMutableTest2>>, ] type errors = [ // @ts-expect-error DeepMutable<'string'>, // @ts-expect-error DeepMutable<0>, ]
🖌 풀이
type DeepMutable<T extends object> = { -readonly [P in keyof T]: T[P] extends object ? T[P] extends (...args: unknown[]) => any ? T[P] : DeepMutable<T[P]> : T[P] }
Mapped 타입에서 Mapping 연산자(-)로 readonly를 재귀적으로 제거해주면 된다.
케이스2 를 참고하면 함수도 object여서 에러가 발생하므로, 이에 대해서만 예외처리를 해주면 된다.
5. All
Returns true if all elements of the list are equal to the second parameter passed in, false if there are any mismatches.
For example
type Test1 = [1, 1, 1] type Test2 = [1, 1, 2] type Todo = All<Test1, 1> // should be same as true type Todo2 = All<Test2, 1> // should be same as false
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<All<[1, 1, 1], 1>, true>>, Expect<Equal<All<[1, 1, 2], 1>, false>>, Expect<Equal<All<['1', '1', '1'], '1'>, true>>, Expect<Equal<All<['1', '1', '1'], 1>, false>>, Expect<Equal<All<[number, number, number], number>, true>>, Expect<Equal<All<[number, number, string], number>, false>>, Expect<Equal<All<[null, null, null], null>, true>>, Expect<Equal<All<[[1], [1], [1]], [1]>, true>>, Expect<Equal<All<[{}, {}, {}], {}>, true>>, Expect<Equal<All<[never], never>, true>>, Expect<Equal<All<[any], any>, true>>, Expect<Equal<All<[unknown], unknown>, true>>, Expect<Equal<All<[any], unknown>, false>>, Expect<Equal<All<[unknown], any>, false>>, ]
🖌 풀이
type All<T extends unknown[], U> = Equal<T[number], U> extends true ? true : false
T[number] 로 유니온으로 접근할 수 있다는 특성을 활용하면 쉽게 풀 수 있다.
6. Filter
Implement the type Filter<T, Predicate> takes an Array T, primitive type or union primitive type Predicate and returns an Array include the elements of Predicate.
/* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type Falsy = false | 0 | '' | null | undefined type cases = [ Expect<Equal<Filter<[0, 1, 2], 2>, [2]>>, Expect<Equal<Filter<[0, 1, 2], 0 | 1>, [0, 1]>>, Expect<Equal<Filter<[0, 1, 2], Falsy>, [0]>>, ]
🖌 풀이
type Filter<T extends any[], P, A extends any[] = []> = T extends [infer F, ...infer R] ? Filter<R, P, (F extends P ? [...A, F] : A)> : A
배열을 전개해서 풀 수 있는 문제였다.
반응형'Front-End(Web) > Typescript' 카테고리의 다른 글
[Type-challenges] 난이도 Hard - (2) (0) 2023.03.25 [Type-challenges] 난이도 Hard - (1) (0) 2023.03.25 [Type-challenges] 난이도 Medium - (10) (0) 2023.02.28 [Type-challenges] 난이도 Medium - (9) (2) 2023.02.28 [Type-challenges] 난이도 Medium - (8) (0) 2023.02.28