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

유한별·2025년 5월 18일
0

타입 챌린지 스터디

목록 보기
19/19
post-thumbnail

[medium] 30970. Is Fixed String Literal Type

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

문제

Sometimes you may want to determine whether a string literal is a definite type. For example, when you want to check whether the type specified as a class identifier is a fixed string literal type.

type Action<ID extends string> = { readonly id: ID };

Since it must be fixed, the following types must be determined as false.

  • never type
  • Union of string literal types
  • Template literal types with embedded string, number, bigint, boolean

Determine whether the given type S is a definite string literal type.

문제 설명

  • 이 문제는 문자열 리터럴 타입이 확실한 타입인지 확인하는 문제
  • 예를 들어, 클래스 식별자로 지정된 타입이 고정된 문자열 리터럴 타입인지 확인

시도 1 (답지 참조)

접근 방식

  • 타입 조합 활용

코드

type MyEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y
  ? 1
  : 2
  ? true
  : false;

type IsFixedStringLiteralType<S extends string> = {} extends Record<S, 1>
  ? false
  : MyEqual<[S], S extends unknown ? [S] : never>;

코드 설명

  • {} extends Record<S, 1>: S가 일반적인 string인지 체크하는 필터 역할(동적 문자열 타입 제외)
  • abc, 123 등 고정된 문자열 타입은 {} extends Record<S, 1> 타입을 만족하지 않음
  • MyEqual<[S], S extends unknown ? [S] : never>: S가 유니온 타입인지 확인(extends unknown이 true일 경우 유니온 분해)

[medium] 34007. Compare Array Length

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

문제

Implement CompareArrayLength to compare two array length(T & U).

If length of T array is greater than U, return 1;
If length of U array is greater than T, return -1;
If length of T array is equal to U, return 0.

문제 설명

  • 두 배열의 길이를 비교하는 문제
  • T 배열의 길이가 U 배열의 길이보다 크면 1을 리턴
  • U 배열의 길이가 T 배열의 길이보다 크면 -1을 리턴
  • 길이가 같으면 0을 리턴

시도 1

접근 방식

  • 먼저 배열의 길이가 같으면 0을 리턴
  • infer로 하나씩 추출해서 먼저 길이가 0이 되는 요소가 나올 때까지 재귀 처리

코드

type CompareArrayLength<
  T extends any[],
  U extends any[]
> = T["length"] extends U["length"]
  ? 0
  : T extends [infer _, ...infer TRest]
  ? U extends [infer _, ...infer URest]
    ? CompareArrayLength<TRest, URest>
    : 1
  : -1;

코드 설명

  • 먼저 배열의 길이가 같으면 0을 리턴
  • 그게 아니면 각자 infer로 하나씩 추출
  • 만약 U 배열이 먼저 끝나면 1을 리턴
  • 만약 T 배열이 먼저 끝나면 -1을 리턴

[medium] 34857. Defined Partial Record

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

문제

Using a Record with union types as keys doesn't allow you to make an object with only some of them

const record: Record<"a" | "b" | "c", number> = { a: 42, b: 10 };
// error: Property 'c' is missing in type '{ a: number; b: number; }'
// but required in type 'Record<"a" | "b" | "c", number>'

Using a Partial Record with union types as keys allows you to make an object without all union members, but makes all keys and values optional, potentially leaving them undefined

const partial: Partial<Record<"a" | "b" | "c", number>> = { a: 42 };
const partialType = typeof partial; // { a?: number | undefined, b? : number | undefined, c? : number | undefined }
const operation = 0 + partial.a; // error: 'partial.a' is possibly 'undefined'
const access = partial.c; // possible, type doesn't know that there is no such key

You need to make a type that takes the best of both worlds, creates all combinations of all the types in the union, so using a key that exists in the object gives you a defined type, but using a key that exists in the union and not in the object throws an error

const best: DefinedPartial<Record<"a" | "b" | "c", number>> = { a: 42 };
const sum = 0 + best.a; // 42
const error = best.b; // error: property 'b' does not exist on type '{ a: number; }'

문제 설명

이 문제는 TypeScript에서 객체의 타입을 더 정확하게 제어하는 방법을 구현
현재 RecordPartial을 사용할 때 각각 다른 문제가 존재함:

  1. Record<"a" | "b" | "c", number>를 사용하면 a, b, c 모두 필수로 있어야 함.
  2. Partial<Record<"a" | "b" | "c", number>>를 사용하면 모든 키가 선택적이 되어 undefined가 될 수 있고, 존재하지 않는 키에 접근해도 타입 에러가 발생하지 않음.

이 문제의 목표는 DefinedPartial 타입을 만들어서:

  • 객체에 있는 키는 정확한 타입을 가지도록 하고
  • 객체에 없는 키는 타입 에러를 발생시키도록 하는 것

예를 들어 { a: 42 }라는 객체가 있을 때, anumber 타입으로 정확히 인식되고, bc에 접근하면 타입 에러가 발생해야 함.

시도 1

접근 방식

  • 조합 만들어주기

코드

type Keys<T> = keyof T & string;

type Subsets<T extends string> = T extends infer U
  ? U extends string
    ? [U] | [U, ...Subsets<Exclude<T, U>>]
    : never
  : never;

type MergeKeys<T, K extends string[]> = K extends [
  infer Head extends keyof T,
  ...infer Tail extends string[]
]
  ? { [P in Head]: T[P] } & MergeKeys<T, Tail>
  : {};

type DefinedPartial<T extends object> = Subsets<Keys<T>> extends infer K
  ? K extends string[]
    ? MergeKeys<T, K>
    : never
  : never;

실패 이유

  • 조합이 아니라, 그냥 부분 집합만 만들어짐

시도 2 (정답 참고)

접근 방식

  • 유니온과 Omit 활용한 타입 조합

코드

type DefinedPartial<T, K extends keyof T = keyof T> = K extends any
  ? T | DefinedPartial<Omit<T, K>>
  : never;

코드 설명

  • K extends any 타입을 활용해 T의 키 값 순회
  • 각 키 값에 대해 T 타입과 DefinedPartial<Omit<T, K>> 타입의 유니온 타입을 만들어줌
  • 이렇게 하면 객체에 있는 키는 정확한 타입을 가지고, 없는 키는 타입 에러를 발생시키는 타입을 만들 수 있음

[35045] Longest Common Prefix

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

문제

Write a type, LongestCommonPrefix that returns the longest common prefix string amongst a tuple of strings.

If there is no common prefix, return an empty string "".

예시

type Common = LongestCommonPrefix<["flower", "flow", "flight"]>;
//   ?^ "fl"

type Uncommon = LongestCommonPrefix<["dog", "racecar", "race"]>;
//   ?^ ""

문제 설명

  • 튜플에 있는 문자열들의 가장 긴 공통 접두사를 반환하는 타입을 구현
  • 공통 접두사가 없으면 빈 문자열 ""을 반환

시도 1

접근 방식

  • 첫 번째 문자열을 기준으로, prefix 확장
  • 각 prefix마다 모든 문자열이 해당 prefix를 가지는지 확인
  • 가장 긴 prefix를 반환

코드

type AllMatch<T extends string[], P extends string> = T extends [
  infer First extends string,
  ...infer Rest extends string[]
]
  ? First extends `${P}${string}`
    ? AllMatch<Rest, P>
    : false
  : true;

type LongestCommonPrefix<
  T extends string[],
  First extends string = T extends [infer F extends string, ...any] ? F : "",
  Prefix extends string = ""
> = First extends `${infer C}${infer Rest}`
  ? AllMatch<T, `${Prefix}${C}`> extends true
    ? LongestCommonPrefix<T, Rest, `${Prefix}${C}`>
    : Prefix
  : Prefix;

코드 설명

  • First를 활용해 첫번째 요소 확인
  • First를 첫 글자와 나머지 글자로 분해
  • 분해된 첫 글자를 Prefix에 추가, 해당 Prefix가 모든 문자열에 존재하는지 확인
  • 모든 문자열에 존재하지 않으면 이전 Prefix를 반환
  • 모든 문자열에 존재하는 경우 First의 첫 번째 글자를 제외한 나머지 글자에 대해 재귀 호출

[medium] 35191. Trace

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

문제

The trace of a square matrix is the sum of the elements on its main diagonal.
However, it's difficult to calculate the sum with type system.
To make things simple, let's return the elements on the main diagonal with union type.

예시

type Arr = [[1, 2], [3, 4]];
type Test = Trace<Arr>; // expected to be 1 | 4

문제 설명

  • 정사각형 행렬의 대각선 요소들의 유니온 타입을 반환하는 타입을 구현

시도 1

접근 방식

  • 기존 25270-Transpose 문제에서 행렬 전치 타입을 구현한 것을 참고
  • 전치 타입을 구현할 때 행렬의 행과 열의 인덱스가 같은 경우만 유니온 타입을 반환하도록 했음
  • 이를 응용해 대각선 요소들의 유니온 타입을 반환하도록 함

코드

type Trace<T extends any[][], R = T["length"] extends 0 ? [] : T[0]> = {
  [X in keyof R]: {
    [Y in keyof T]: X extends Y ? T[Y][Y] : never;
  };
};

실패 이유

  • T[Y][Y] 에러 ('Y' 형식을 인덱스 형식 'T[Y]'에 사용할 수 없습니다.)

시도 2 (정답)

접근 방식

  • 그냥 인덱스를 활용해서 처리해보자

코드

type Trace<
  T extends any[][],
  IndexArr extends unknown[] = [],
  R = never
> = IndexArr["length"] extends T["length"]
  ? R
  : Trace<
      T,
      [...IndexArr, unknown],
      R | T[IndexArr["length"]][IndexArr["length"]]
    >;

코드 설명

  • IndexArr 타입을 활용해 행렬의 인덱스를 순회, IndexArr의 길이가 T의 길이와 같아지면 종료
  • T[IndexArr["length"]][IndexArr["length"]] 타입을 활용해 대각선 요소 타입 반환
  • 재귀를 통해 대각선 요소들의 유니온 타입을 반환

[medium] 35252. IsAlphabet

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

문제

Determine if the given letter is an alphabet.

문제 설명

  • 주어진 문자가 알파벳인지 확인하는 타입을 구현

시도 1

접근 방식

  • 알파벳 직접 비교

코드

type Alphabet =
  | "A"  | "B"  | "C"  | "D"  | "E"  | "F"  | "G"  | "H"  | "I"  | "J"  | "K"  | "L"  | "M"  | "N"  | "O"  | "P"  | "Q"  | "R"  | "S"  | "T"  | "U"  | "V"  | "W"  | "X"  | "Y" | "Z"
  | "a"  | "b"  | "c"  | "d"  | "e"  | "f"  | "g"  | "h"  | "i"  | "j"  | "k"  | "l"  | "m"  | "n"  | "o"  | "p"  | "q"  | "r"  | "s"  | "t"  | "u"  | "v"  | "w"  | "x"  | "y"  | "z";

type IsAlphabet<S extends string> = S extends Alphabet ? true : false;

코드 설명

  • 알파벳 문자열을 유니온 타입으로 정의
  • 주어진 문자가 알파벳 문자열 유니온 타입에 속하는지 확인
profile
세상에 못할 일은 없어!

0개의 댓글