[TS] Type-Challenges 스터디 7주차

유한별·2025년 3월 14일
0
post-thumbnail

[Medium] 645. Diff

문제

O & O1의 차이점인 객체를 가져옵니다

예시

import type { Equal, Expect } from "@type-challenges/utils";

type Foo = {
  name: string;
  age: string;
};
type Bar = {
  name: string;
  age: string;
  gender: number;
};
type Coo = {
  name: string;
  gender: number;
};

type cases = [
  Expect<Equal<Diff<Foo, Bar>, { gender: number }>>,
  Expect<Equal<Diff<Bar, Foo>, { gender: number }>>,
  Expect<Equal<Diff<Foo, Coo>, { age: string; gender: number }>>,
  Expect<Equal<Diff<Coo, Foo>, { age: string; gender: number }>>,
];

시도 1

접근 방식

  • A 객체의 키를 순회하며 B 객체에 extends가 안될 경우 빈 객체에 키-값 쌍 추가
  • 마찬가지로 B 객체에 A 객체에 extends가 안될 경우 빈 객체에 키-값 쌍 추가
  • 위 두 객체의 결과를 유니온한 뒤 객체로 리턴

코드

type GetDiff<A, B> = { [K in keyof A]: A[K] extends B ? A[K] : never };

type Diff<O, O1, DiffIntersection = GetDiff<O, O1> & GetDiff<O1, O>> = {
  [K in keyof DiffIntersection]: DiffIntersection[K];
};

type example1 = Diff<Foo, Bar>;
// { name: never; age: never; gender: never; }

실패 이유

  • value가 never가 되는건 속성을 없애지는 않아서 key: never로 처리됨

시도 2 (정답)

접근 방식

  • 시도 1과 접근 방식은 동일, as(key-remapping)을 활용해 value가 아니라 key값을 never 처리하도록 변경

코드

type GetDiff<A, B> = { [K in keyof A as K extends keyof B ? never : K]: A[K] };

type Diff<O, O1, DiffIntersection = GetDiff<O, O1> & GetDiff<O1, O>> = {
  [K in keyof DiffIntersection]: DiffIntersection[K];
};

코드 설명

  • GetDiff 함수는 첫번째 인자의 키를 순회하며 두번째 인자에 포함되지 않는 키를 never로 처리한 객체를 리턴
  • DiffIntersectionGetDiff<A, B>GetDiff<B, A>의 유니온 객체
  • 이후 DiffIntersection의 키를 순회하며 각 키의 값을 리턴

[Medium] 949. AnyOf

문제

Python의 any function을 타입 시스템으로 구현하세요

배열을 사용하고 배열의 요소가 참이면 true를 반환합니다. 배열이 비어 있으면 false를 반환합니다

예시

type Sample1 = AnyOf<[1, "", false, [], {}]>; // expected to be true.
type Sample2 = AnyOf<[0, "", false, [], {}]>; // expected to be false.

시도 1

접근 방식

  • 빈 배열인지 확인한 후, 빈 배열이 아닐 경우 재귀를 이용해 각 요소를 순회하며 단 한 번이라도 false 값을 가질 경우 false 리턴
  • false 값인지는 따로 객체를 만들어 확인

코드

type IsFalse = 0 | "" | false | [] | {} | undefined | null;
type AnyOf<T extends readonly any[]> = T extends []
  ? false
  : T extends [infer First, ...infer Rest]
    ? First extends IsFalse
      ? AnyOf<Rest>
      : true
    : false;

실패 이유

  • IsFalse에 들어있는 {}가 모든 객체를 false로 잡아버림

시도 2 (정답)

접근 방식

  • 기존 방식에서, isEmptyObject라는 타입을 만들어 빈 객체를 false로 처리하도록 변경

코드

type IsEmptyObject<T> = keyof T extends never ? true : false;
type IsFalse<T> = T extends 0 | "" | false | [] | undefined | null
  ? true
  : T extends object
    ? IsEmptyObject<T>
    : false;

type AnyOf<T extends readonly any[]> = T extends []
  ? false
  : T extends [infer First, ...infer Rest]
    ? IsFalse<First> extends true
      ? AnyOf<Rest>
      : true
    : false;

코드 설명

  • IsEmptyObjectkeyof Tnever(키가 존재하지 않는 빈 객체)일 경우 true를 리턴하는 타입
  • IsFalse는 비어있는 배열, 0, "", false, undefined, null, 그리고 빈 객체를 true로 처리하는 타입
  • AnyOf는 빈 배열일 경우 false를 리턴
  • 비어있지 않을 경우 재귀를 이용해 각 요소를 순회하며 단 한 번이라도 IsFalse 타입이 false 값을 가질 경우 false 리턴

[Medium] 1042. IsNever

문제

input type으로 T를 받는 IsNever type을 구현하세요. 만약 T의 유형이 never으로 확인되면 true를 반환하고 아니면 false를 반환합니다

예시

type A = IsNever<never>; // expected to be true
type B = IsNever<undefined>; // expected to be false
type C = IsNever<null>; // expected to be false
type D = IsNever<[]>; // expected to be false
type E = IsNever<number>; // expected to be false

시도

접근 방식

  • Tnever일 경우 true를 리턴하고 아니면 false를 리턴

코드

type IsNever<T> = [T] extends [never] ? true : false;

코드 설명

  • [T] extends [never]Tnever일 경우 true를 리턴하고 아니면 false를 리턴(분배법칙 X)

[Medium] 1097. IsUnion

문제

T를 입력으로 받고, TUnion 유형으로 확인되는지 여부를 반환하는 IsUnion을 구현하세요

예시

type case1 = IsUnion<string>; // false
type case2 = IsUnion<string | number>; // true
type case3 = IsUnion<[string | number]>; // false

시도 1

접근 방식

  • 유니온 타입을 튜플로 변환 후, 튜플의 길이 확인
  • 튜플의 길이가 1보다 크면 유니온 아닐까?

코드

type UnionToTuple<T, K = T> = [T] extends [never]
  ? []
  : K extends K
    ? [K, ...UnionToTuple<Exclude<T, K>>]
    : never;

type IsUnion<T> = UnionToTuple<T>["length"] extends [any] ? true : false;

type example = UnionToTuple<string | number | boolean>;

실패 이유

  • 튜플이 아니라 퍼머넌트로 나옴

시도 2 (정답)

접근 방식

  • 분배법칙을 활용해 각 요소에 접근
  • 각 요소가 전체 타입(입력받은 T)과 같으면 false, 다르면 true

코드

type IsUnion<T, U = T> = [T] extends [never]
  ? false
  : T extends U
    ? [U] extends [T]
      ? false
      : true
    : never;

코드 설명

  • [T] extends [never]Tnever인 경우 분기처리
  • T extends U를 활용해 각 요소를 순회(항상 참인 조건, 분배법칙만을 위한 요소)
  • 만약 각 요소와 전체 타입(U = 입력받은 T)이 같다면 이는 유니온 타입이 아니기 때문에 false를 반환
  • 각 요소와 전체 타입이 다르다면 이는 유니온 타입이기 때문에 true를 반환

[Medium] 1130. ReplaceKeys

문제

Union type의 key를 대체하는 ReplaceKeys를 구현하세요.
만약 일부 유형에 해당 key가 존재하지 않는다면 대체하지 않습니다. 타입은 세 개의 인자를 받습니다.

예시

type NodeA = {
  type: "A";
  name: string;
  flag: number;
};

type NodeB = {
  type: "B";
  id: number;
  flag: number;
};

type NodeC = {
  type: "C";
  name: string;
  flag: number;
};

시도 1

접근 방식

  • 유니온 타입을 순회하며 각 요소에 대해 T를 순회하며 키 값이 존재하면 Y[T]를, 존재하지 않는다면 never를 넣어주는 방식

코드

type ReplaceKeys<U, T extends keyof any, Y> = U extends U
  ? T extends T
    ? T extends keyof U
      ? T extends keyof Y
        ? Y[T]를 추가한 U
        : never
      : Record<T, never>를 추가한 U
    : never
  : never;

실패 이유

  • Y[T]를 추가한 U, Record<T, never>를 추가한 U를 구현할 방법을 못찾음

시도 2 (답지 확인)

접근 방식

코드

type ReplaceKeys<U, T extends keyof any, Y> = {
  [K in keyof U]: K extends T ? (K extends keyof Y ? Y[K] : never) : U[K];
};

코드 설명

  • T는 키값이므로 extends keyof any로 선언
  • K in keyof U는 유니온 타입으로 묶인 각 객체들의 키들을 모두 순회
  • 각 키에 대해 K extends T 조건을 만족할 경우(T가 객체의 키 값일 경우), 다음 조건 확인
  • KY의 키 값일 경우, Y[K]를 반환, 아닐 경우 never를 반환
  • KT의 키 값이 아닐 경우, U[K]를 반환

[Medium] 1367. Remove Index Signature

문제

객체 유형에서 인덱스 시그니처를 제외하는 RemoveIndexSignature<T>를 구현하세요

예시

type Foo = {
  [key: string]: any;
  foo(): void;
};

type A = RemoveIndexSignature<Foo>; // expected { foo(): void }

시도 1

접근 방식

  • key-remapping으로 키 값이 [key: any] 형태를 걸러내면 되지 않을까?

코드

type RemoveIndexSignature<T> = {
  [K in keyof T as K extends [key: keyof any] ? never : K]: T[K];
};

실패 이유

  • [key: keyof any] 형태 걸러내기 실패

시도 2 (답지 확인)

접근 방식

코드

type RemoveIndexSignature<T, P = PropertyKey> = {
  [K in keyof T as P extends K ? never : K extends P ? K : never]: T[K];
};

코드 설명

  • PPropertyKey로 선언
  • key-remapping으로 P extends K 조건을 만족하는 경우, 해당 키 값을 제거
  • P extends K 조건을 만족한다는 것은 KP(string | number | symbol)의 슈퍼타입이라는 것
  • 즉, Kstring, number, symbol이면 never을 반환하여 키를 삭제
  • 이후 K extends P 조건을 만족하는 경우(K가 P의 서브타입일 경우, 예를 들어 'foo', 123, symbol 등), K를 반환하여 키를 유지

추가 질문

PropertyKey 타입이란 무엇인가?

  • keyof any와 같은 타입
  • 문자열, 숫자, 심볼 타입을 포함하는 유니온 타입

Index Signature란 무엇인가?

profile
세상에 못할 일은 없어!

0개의 댓글