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

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

[Medium] 110. Capitalize

View on GitHub: https://tsch.js.org/110

문제

문자열의 첫 글자만 대문자로 바꾸고 나머지는 그대로 놔두는 Capitalize<T>를 구현하세요.

정답

type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${Uppercase<First>}${Rest}`
  : "";

시도했던 오답

// 1차 시도
type MyCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${First["toUpperCase"]}${Rest}`
  : "";

설명

  • infer 키워드를 사용하여 문자열을 첫 글자와 나머지 문자열로 분리
  • 첫 글자를 Uppercase 타입으로 변환 (S["toUpperCase"]가 될 줄 알았음...)
  • 나머지 문자열을 첫 글자와 합쳐서 반환

Reference

[Medium] 116. Replace

View on GitHub: https://tsch.js.org/116

문제

문자열 S에서 From를 찾아 한 번만 To로 교체하는 Replace<S, From, To>를 구현하세요.

정답

type Replace<
  S extends string,
  From extends string,
  To extends string,
> = From extends ""
  ? S
  : S extends `${infer RestHead}${From}${infer RestTail}`
    ? `${RestHead}${To}${RestTail}`
    : S;

시도했던 오답

// 1차 시도
type Replace<
  S extends string,
  From extends string,
  To extends string,
> = S extends `${infer RestHead}${From}${infer RestTail}`
  ? `${RestHead}${To}${RestTail}`
  : S;

설명

  • From이 빈 문자열인 경우 원래 문자열을 반환
  • infer 키워드를 사용하여 문자열을 RestHead, From, RestTail로 분리
  • SFrom을 포함하는 경우(조건이 True 일 때) FromTo로 교체하고 나머지 문자열을 합쳐서 반환
  • From이 빈 문자열이 아니고 SFrom을 포함하지 않는 경우 원래 문자열을 반환

[Medium] 116. ReplaceAll

View on GitHub: https://tsch.js.org/119

문제

문자열 S에서 From를 찾아 모두 To로 교체하는 ReplaceAll<S, From, To>를 구현하세요.

정답

type ReplaceAll<
  S extends string,
  From extends string,
  To extends string,
> = From extends ""
  ? S
  : S extends `${infer RestHead}${From}${infer RestTail}`
    ? `${RestHead}${To}${ReplaceAll<RestTail, From, To>}`
    : S;

시도했던 오답

// 1차 시도
type ReplaceAll<
  S extends string,
  From extends string,
  To extends string,
> = S extends `${infer RestHead}${From}${infer RestTail}`
  ? ReplaceAll<`${RestHead}${To}${RestTail}`, From, To>
  : S;

설명

  • From이 빈 문자열인 경우 원래 문자열을 반환
  • infer 키워드를 사용하여 문자열을 RestHead, From, RestTail로 분리
  • SFrom을 포함하는 경우(조건이 True 일 때) FromTo로 교체하고 From 이후 문자열(RestTail)을 재귀적으로 처리
  • From이 빈 문자열이 아니고 SFrom을 포함하지 않는 경우 원래 문자열을 반환

[Medium] 191. Append Argument

View on GitHub: https://tsch.js.org/191

문제

함수 타입 Fn과 어떤 타입 A가 주어질 때 Fn의 인수와 A를 마지막 인수로 받는 Fn과 동일한 함수 유형인 G를 생성하세요.

정답

type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (
  ...args: infer Args
) => infer Return
  ? (...args: [...Args, A]) => Return
  : never;

시도했던 오답

// 1차 시도
type AppendArgument<Fn extends (...args: any[]) => any, A> = Fn extends (
  ...args: infer Args
) => infer Return
  ? ([Args, A]) => Return
  : never;

설명

  • 우선 Fn에서 인수와 반환 타입을 추출해야 됨
  • [easy] 3312-parameters 문제를 참고하여 인수를 추출
  • [medium] 2-return-type 문제를 참고하여 반환 타입을 추출
  • 추출된 인수에 A를 추가하여 반환 타입을 반환하는 함수 타입을 생성
  • 이 때 추출된 인수는 배열로 추출되므로 스프레드 배열에 A를 추가

Reference

  • [easy] 3312-parameters
  • [medium] 2-return-type

[Medium] 296. Permutation

View on GitHub: https://tsch.js.org/296

문제

주어진 유니언 타입을 순열 배열로 바꾸는 Permutation 타입을 구현하세요.

정답

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

설명

  1. <T, K = T>: T를 기본값으로 설정하는 변수 K 생성

    • K extends K에서 유니온 분배를 위해 사용
  2. [T] extends [never]: Tnever인 경우를 체크

    • Tnever인 경우, 빈 배열을 반환해서 순열 생산 종료 (재귀 종료)
    • T extends never를 사용하지 않는 이유는 분배법칙을 막기 위해서
  3. K extends K: T와 동일한 K를 유니온 분배하여 순회

    • K(유니온)를 순회하며 각 요소에 대해 재귀 호출
  4. [K, ...Permutation<Exclude<T, K>>]: K를 제외한 T의 순열을 구하는 재귀 호출

    • K extends K에서 분배된 요소를 순회하며 재귀 호출
    • Exclude<T, K>: T에서 K를 제외한 타입으로 재귀 호출
type Test = Permutation<"A" | "B" | "C">;

// 1단계: K = 'A'
// 결과: ['A', ...Permutation<'B' | 'C'>]

// 2단계: K = 'B'
// 결과: ['B', ...Permutation<'A' | 'C'>]

// 3단계: K = 'C'
// 결과: ['C', ...Permutation<'A' | 'B'>]

추가 질문

K extends K 제대로 이해하기

  • K extends K는 항상 참이므로, 결국 모든 유니온 분배를 트리거하는 역할
type Test<K> = K extends K ? [K] : never;

type Result = Test<"A" | "B" | "C">;
  • 이 예시의 경우 다음과 같이 동작함
'A' extends 'A' ? ['A'] : never['A']
'B' extends 'B' ? ['B'] : never['B']
'C' extends 'C' ? ['C'] : never['C']
  • 그리고 해당 결과를 유니온으로 합쳐 ['A'] | ['B'] | ['C'] 반환

  • 만약 분배법칙을 적용하지 않으려면 K를 배열로 감싸서 [K]로 표현해야 함

type NoDistribute<K> = [K] extends [K] ? [K] : never;

type Result = NoDistribute<"A" | "B" | "C">;
// Result는 ['A' | 'B' | 'C']

Reference

[Medium] 298. Length Of String

View on GitHub: https://tsch.js.org/298

문제

String#length처럼 동작하는 문자열 리터럴의 길이를 구하세요.

정답

type StringToTuple<S extends String> = S extends `${infer First}${infer Last}`
  ? [First, ...StringToTuple<Last>]
  : [];

type LengthOfString<S extends string> = StringToTuple<S>["length"];

설명

  • StringToTuple 함수는 문자열을 튜플로 변환하는 함수
  • String 타입이 인자로 들어오면 한 글자씩 나눠 튜플로 변환
  • 그렇게 구해진 튜플의 길이를 반환하면 문자열의 길이를 구할 수 있음
profile
세상에 못할 일은 없어!

0개의 댓글