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

유한별·2025년 4월 13일
0

타입 챌린지 스터디

목록 보기
14/19
post-thumbnail

[medium] 8767. Combination

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

문제

Given an array of strings, do Permutation & Combination.
It's also useful for the prop types like video controlsList

// expected to be `"foo" | "bar" | "baz" | "foo bar" | "foo bar baz" | "foo baz" | "foo baz bar" | "bar foo" | "bar foo baz" | "bar baz" | "bar baz foo" | "baz foo" | "baz foo bar" | "baz bar" | "baz bar foo"`
type Keys = Combination<["foo", "bar", "baz"]>;

문제 설명

  • string 배열을 받아, combination을 만들어 반환

시도 1 (정답)

접근 방식

    1. All Combination 참고하여 같은 원리로 접근
  • 참고: All Combination

코드

type MakeCombination<A extends string, B extends string> =
  | A
  | B
  | `${A} ${B}`
  | `${B} ${A}`;

type UnionCombination<A extends string, B extends string = A> = A extends B
  ? MakeCombination<A, UnionCombination<Exclude<B, A>>>
  : never;

type Combination<T extends string[]> = UnionCombination<T[number]>;

코드 설명

  • MakeCombination : 두 문자열을 받아, 순서를 바꾸어 조합하여 반환
  • UnionCombination : 문자열 유니온을 순회하며, 각 요소와 나머지 요소를 MakeCombination 함수에 전달하여 조합
  • Combination : 문자열 배열을 받아, UnionCombination에 유니온으로 변환하여 넣어줌

[medium] 8987. Subsequence

문제

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

A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements.

예시

type A = Subsequence<[1, 2]>; // [] | [1] | [2] | [1, 2]

문제 설명

  • 배열을 받아, 모든 부분 수열을 반환

시도 1

접근 방식

  • 기존 콤비네이션 코드 활용
  • 튜플을 유니온으로 만든 뒤, 각 요소로 콤비네이션 만들어서 리턴?

코드

type Combination<A extends string, B extends string> = [A] | [B] | [A, B];

type UnionCombination<A extends string, B extends string = A> = A extends B
  ? Combination<A, UnionCombination<Exclude<B, A>>>
  : never;

type Subsequence<T extends any[]> = UnionCombination<T[number]>;

실패 이유

  • 콤비네이션으로 구현할 수 없음

시도 2 (답지 참고)

접근 방식

  • 순회 + Exclude 대신 재귀로 구현

코드

type Subsequence<T extends any[]> = T extends [infer First, ...infer Rest]
  ? Subsequence<Rest> | [First, ...Subsequence<Rest>]
  : [];

코드 설명

  • 배열을 순회하며, 각 요소를 추출
    1. 해당 요소와 나머지의 부분으로 만든 부분 수열과
    1. 해당 요소를 제외한 나머지의 부분으로 만든 부분 수열을 합쳐 유니온으로 리턴
  • First가 없을 경우(빈 배열일 경우) 빈 배열 리턴

[medium] 9142. Check Repeated Chars

문제

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

Implement type CheckRepeatedChars<S> which will return whether type S contains duplicated chars?

예시

type CheckRepeatedChars<'abc'>   // false
type CheckRepeatedChars<'aba'>   // true

문제 설명

  • 문자열을 받아, 중복된 문자가 있는지 확인

시도 1 (정답)

접근 방식

  • 중복 관리하는 유니온을 하나 만들고, 문자열을 순회하며 중복된 문자가 있는지 확인

코드

type CheckRepeatedChars<
  T extends string,
  Dup = ""
> = T extends `${infer First}${infer Rest}`
  ? First extends Dup
    ? true
    : CheckRepeatedChars<Rest, Dup | First>
  : false;

코드 설명

  • Dup은 중복 관리하는 유니온
  • 문자열을 순회하며, Dup에 해당 문자가 있는지 확인(extends)
  • 중복된 문자가 있으면 true, 없으면 Dup에 해당 문자 추가해 재귀

[medium] 9286. First Unique Char Index

문제

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

Given a string s, find the first non-repeating character in it and return its index. If it does not exist, return -1. (Inspired by leetcode 387)

문제 설명

  • 문자열을 받아, 첫 번째 유니크한 문자의 인덱스를 반환
  • 유니크한 문자가 없으면 -1 반환

시도 1 (정답)

접근 방식

  • 문자열을 순회하며, 중복된 문자가 있는지 확인
  • 중복된 문자가 있으면 index를 반환하고, 아닐 경우 -1 반환

코드

// C가 T에서 유니크한지 확인
type IsUnique<
  T extends string,
  C extends string,
  Count extends unknown[] = []
> = T extends `${infer First}${infer Rest}`
  ? C extends First
    ? IsUnique<Rest, C, [...Count, unknown]>
    : IsUnique<Rest, C, Count>
  : Count["length"] extends 1
  ? true
  : false;

// 각 글자마다 전체 중복검사하면서, 중복 아니면 인덱스 반환
type FirstUniqueCharIndex<
  T extends string,
  Original extends string = T,
  IndexArr extends unknown[] = []
> = T extends `${infer First}${infer Rest}`
  ? IsUnique<Original, First> extends true
    ? IndexArr["length"]
    : FirstUniqueCharIndex<Rest, Original, [...IndexArr, unknown]>
  : -1;

코드 설명

  • IsUnique 타입은 문자열에서 유니크한 문자가 있는지 확인
  • C는 확인할 문자(Character), T를 순회하며 CT의 요소인지 infer를 활용해 확인
  • Count는 중복 횟수를 카운트하는 배열. 중복되면 배열에 요소를 추가하고, 중복되지 않으면 배열에 요소를 추가하지 않음 (중복 횟수가 1인지 확인)
  • FirstUniqueCharIndex 타입은 문자열을 순회하며, IsUnique 타입을 활용해 유니크한 문자인지 확인
  • 유니크한 문자일 경우 IndexArr 배열의 길이를 반환, 아닐 경우 재귀 호출
    SP

[medium] 9616. Parse URL Params

문제

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

You're required to implement a type-level parser to parse URL params string into an Union.

예시

ParseUrlParams<":id">; // id
ParseUrlParams<"posts/:id">; // id
ParseUrlParams<"posts/:id/:user">; // id | user

문제 설명

  • 문자열을 받아, 파라미터를 파싱하여 유니온 타입으로 반환

시도 1

접근 방식

  • 템플릿 리터럴을 활용해, first, :, param, /, rest로 나누어 파싱

코드

type ParseUrlParams<T> = T extends `${infer _}:${infer Param}/:${infer Rest}`
  ? Param | ParseUrlParams<Rest>
  : T extends `${infer _}:${infer Param}`
  ? Param extends `${infer First}/`
    ? First
    : Param
  : never;

코드 설명

  • 템플릿 리터럴을 활용해, first, :, param, /, rest로 나누어 파싱
  • 조건부를 활용해 :, /, /: 각 조건의 맞게 확인

실패 이유

  • 분기처리가 너무 많아져서 복잡해짐
  • 더 간단하게 해결할 방법이 있을 것 같음

시도 2 (정답)

접근 방식

  • / 단위로 나누고, 그다음에 : 가 붙어있는지 확인

코드

type ParseUrlParams<T> = T extends `${infer Start}/${infer Rest}`
  ? ParseUrlParams<Start> | ParseUrlParams<Rest>
  : T extends `:${infer Param}`
  ? Param
  : never;

코드 설명

  • / 단위로 나누고, 그다음에 : 가 붙어있는지 확인
  • : 가 붙어있으면, 그 문자를 유니온 타입으로 반환
  • : 가 붙어있지 않으면, never 반환

[medium] 9896. Get Middle Element

문제

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

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

예시

  type 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]

문제 설명

  • 배열을 받아, 중간 요소를 반환
  • 배열의 길이가 홀수면, 중간 요소를 반환
  • 배열의 길이가 짝수면, 중간 두 요소를 반환

시도 1

접근 방식

  • infer를 활용해 양쪽 요소를 하나씩 제거하면서 중간 요소를 찾음

코드

type GetMiddleElement<T extends any[]> = T extends [
  infer _F,
  ...infer R,
  infer _L
]
  ? GetMiddleElement<R>
  : T;

코드 설명

  • infer _Finfer _L은 양쪽 요소를 제거하기 위해 사용
  • ...infer R은 중간 요소를 찾기 위해 사용

실패 이유

  • 배열의 길이가 짝수일 때는 빈 배열 반환

시도 2 (정답)

접근 방식

  • 배열의 길이가 2일 때 얼리 리턴

코드

type GetMiddleElement<T extends any[]> = T extends [infer F, infer L]
  ? [F, L]
  : T extends [infer _F, ...infer R, infer _L]
  ? GetMiddleElement<R>
  : T;

코드 설명

  • 배열의 길이가 2일 때(infer F, infer L) 얼리 리턴
  • 이외에는 양쪽 요소를 하나씩 제거하면서 중간 요소를 찾음
profile
세상에 못할 일은 없어!

0개의 댓글