-
[Type-challenges] 난이도 Easy - (2)Front-End(Web)/Typescript 2023. 1. 25. 01:07반응형
Github 챌린지 문제들을 풀고 관련된 내용을 정리하면서, 부족했던 타입스크립트 기본지식을 다지고자 한다. (주 1-2회)
https://github.com/type-challenges/type-challenges
📘 목차 - Easy
- If
- Concat
- Includes
- Push
- Unshift
- Parameters
📘 문제 및 풀이
1. If
Implement the util type If<C, T, F> which accepts condition C, a truthy value T, and a falsy value F. C is expected to be either true or false while T and F can be any type.
For example:type A = If<true, 'a', 'b'> // expected to be 'a' type B = If<false, 'a', 'b'> // expected to be 'b' /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<If<true, 'a', 'b'>, 'a'>>, Expect<Equal<If<false, 'a', 2>, 2>>, ] // @ts-expect-error type error = If<null, 'a', 'b'>
🖌 풀이
type If<C extends boolean, T, F> = C extends true ? T : F;
If 유틸리티 타입을 직접 작성하는 코드다. C가 true면 T 아니면 F 타입을 조건부로 반환해준다.
단, 에러 케이스처럼 C가 Boolean 타입임을 제약해서, 이외의 경우엔 에러를 발생시킨다.
2. Concat
Implement the JavaScript Array.concat function in the type system. A type takes the two arguments. The output should be a new array that includes inputs in ltr order
For example:type Result = Concat<[1], [2]> // expected to be [1, 2] /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Concat<[], []>, []>>, Expect<Equal<Concat<[], [1]>, [1]>>, Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>, Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>, ]
🖌 풀이
type Concat<T extends any[], U extends any[]> = [...T, ...U]
T, U 타입이 각각 배열임을 명시해주고, 이를 spread한 배열(타입)을 생성하면 된다.
3. Includes
Implement the JavaScript Array.includes function in the type system. A type takes the two arguments. The output should be a boolean true or false.
For example:type isPillarMen = Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'> // expected to be `false` /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>, Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>, Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>, Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>, Expect<Equal<Includes<[1, 2, 3], 2>, true>>, Expect<Equal<Includes<[1, 2, 3], 1>, true>>, Expect<Equal<Includes<[{}], { a: 'A' }>, false>>, Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>, Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>, Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>, Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>, Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>, Expect<Equal<Includes<[1], 1 | 2>, false>>, Expect<Equal<Includes<[1 | 2], 1>, false>>, Expect<Equal<Includes<[null], undefined>, false>>, Expect<Equal<Includes<[undefined], null>, false>>, ]
🖌 풀이
1) infer 추론 + 재귀를 활용한 풀이type Includes<T extends readonly any[], U> = T extends [infer First, ...infer Rest] ? MyEqual<First, U> extends true ? true : Includes<Rest, U> : false type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false
먼저, MyEqual 이라는 타입으로 Equal 유틸리티 타입을 직접 구현했다. 두 값이 같으면 true, 아니면 false 를 반환한다.
이제, Includes 타입은 T의 엘리먼트들에 대해 재귀적으로 MyEqual을 돌리면서 U와 비교한다.
이 때, infer를 통해 T배열의 첫 번째 인자와 나머지 인자들을 추론하게끔 한다.
2) 배열을 객체화한 풀이
type Includes<T extends readonly any[], U> = { [t in keyof T]: Equal<T[t], U> }[number] extends false ? false : true
t는 T 배열타입의 인덱스가 된다.(in keyof)
이 인덱스(t)를 key로, T[t] 엘리먼트와 U의 Equal 여부를 value로 하는 객체 타입을 만들고,
이것의 [number]로 각 키를 순회 해서 Equal 여부를 반환해주면 된다.
* in keyof
in 키워드는 index signatures 로 정의된다. 보통, 유니온 타입에 해당 타입이 포함되는지 여부를 연산하기에 Type Guard에 쓰인다.
interface A { x: number; } interface B { y: string; } let q: A | B = ...; if ('x' in q) { // q: A } else { // q: B }
in 키워드가 keyof와 결합되면, 배열에서의 mapped type definition 으로도 정의되며이는 배열의 각 인자를 다시 맵핑할 수 있다.
interface Person { name: string; age: number; } type Partial<T> = { [P in keyof T]?: T[P]; // P will be each key of T } type PersonPartial = Partial<Person>; // same as { name?: string; age?: number; }
(참고링크)
4. Push
Implement the generic version of Array.push
For example:type Result = Push<[1, 2], '3'> // [1, 2, '3'] /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Push<[], 1>, [1]>>, Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>, Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>, ]
🖌 풀이
type Push<T extends any[], U> = [...T, U]
위 2번의 Concat의 풀이를 참고했다. T가 배열임을 extends로 명시한 다음 spread 하고, 거기에 U를 추가한 타입이다.
5. Unshift
Implement the type version of Array.unshift
For example:type Result = Unshift<[1, 2], 0> // [0, 1, 2,] /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Unshift<[], 1>, [1]>>, Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2]>>, Expect<Equal<Unshift<['1', 2, '3'], boolean>, [boolean, '1', 2, '3']>>, ]
🖌 풀이
type Unshift<T extends any[], U> = [U, ...T]
위 4번의 Push와 아주 유사한 풀이이다. Unshift는 인자를 앞에 추가하므로, U와 spread 순서만 바꿔주면 된다.
6. Parameters
Implement the built-in Parameters generic without using it.
For example:const foo = (arg1: string, arg2: number): void => {} type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number] /* _____________ Test Cases _____________ */ import type { Equal, Expect } from '@type-challenges/utils' const foo = (arg1: string, arg2: number): void => {} const bar = (arg1: boolean, arg2: { a: 'A' }): void => {} const baz = (): void => {} type cases = [ Expect<Equal<MyParameters<typeof foo>, [string, number]>>, Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>, Expect<Equal<MyParameters<typeof baz>, []>>, ]
🖌 풀이
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
Parameters 유틸리티 타입을 직접 구현하는 풀이다.
T가 함수타입이 맞다면, infer로 추론한 인자들의 타입(배열)인 P를 반환하고 아니면 never(혹은 false)를 반환한다.
반응형'Front-End(Web) > Typescript' 카테고리의 다른 글
[Type-challenges] 난이도 Medium - (2) (0) 2023.02.14 [Type-challenges] 난이도 Medium - (1) (0) 2023.02.06 [Type-challenges] 난이도 Easy - (1) (0) 2023.01.24 <작성중>[Typescript] Utility Type(유틸리티 타입) (0) 2022.07.25 [Typescript] 타입 단언 / 타입 가드 / 타입 호환 (0) 2022.05.13