ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Type-challenges] 난이도 Easy - (2)
    Front-End(Web)/Typescript 2023. 1. 25. 01:07
    반응형

    Github 챌린지 문제들을 풀고 관련된 내용을 정리하면서, 부족했던 타입스크립트 기본지식을 다지고자 한다. (주 1-2회)

     

    https://github.com/type-challenges/type-challenges

     

    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

     


    📘 목차 - Easy

    1. If
    2. Concat
    3. Includes
    4. Push
    5. Unshift
    6. 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)를 반환한다.

     

     

    반응형
Designed by Tistory.