icon

메티의 블로그

6주차 타입챌린지 스터디
6주차 타입챌린지 스터디

6주차 타입챌린지 스터디

Tags
TypeScript
날짜
Feb 12, 2025
상태
공개

459 Flattern

문제: 주어진 배열을 플랫한 배열 타입으로 바꾸는 Flatten 타입을 구현하세요.
/* _____________ 여기에 코드 입력 _____________ */ // type Flatten<T extends unknown[], C = []> = {[index in keyof T]: T[index] extends [infer Inner] ? (Inner extends unknown[] ? Flatten<Inner, [C, ...Inner]> : Inner) : T[index]} type Flatten<T extends unknown[]> = T extends [infer First, ...infer Rest] ? First extends unknown[] ? [...Flatten<First>, ...Flatten<Rest>] : [First, ...Flatten<Rest>] : []; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Flatten<[]>, []>>, Expect<Equal<Flatten<[1, 2, 3, 4]>, [1, 2, 3, 4]>>, Expect<Equal<Flatten<[1, [2]]>, [1, 2]>>, Expect<Equal<Flatten<[1, 2, [3, 4], [[[5]]]]>, [1, 2, 3, 4, 5]>>, Expect<Equal<Flatten<[{ foo: 'bar', 2: 10 }, 'foobar']>, [{ foo: 'bar', 2: 10 }, 'foobar']>>, ] // @ts-expect-error type error = Flatten<'1'>
  • 배운 점
    • 배열의 경우 infer 로 꺼내지 않고, …Arr 형태로 꺼내도 된다.

527 Append to object

문제: 주어진 인터페이스에 새로운 필드를 추가한 object 타입을 구현하세요. 이 타입은 세 개의 인자를 받습니다.
/* _____________ 여기에 코드 입력 _____________ */ // type AppendToObject<T, U extends keyof any, V> = T & { [K in U]: V } // type AppendToObject<T, U, V> = {[index in keyof (T & U)]: index extends keyof T ? T[index] : V} type AppendToObject<T, U extends keyof any, V> = {[index in keyof T | U]: index extends keyof T ? T[index] : V} type Test = AppendToObject<test1, 'home', boolean> type A = { a: string; } type B = { b: string; } type C = A & B; type D = { a: string; b: string; } /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type test1 = { key: 'cat' value: 'green' } type testExpect1 = { key: 'cat' value: 'green' home: boolean } type test2 = { key: 'dog' | undefined value: 'white' sun: true } type testExpect2 = { key: 'dog' | undefined value: 'white' sun: true home: 1 } type test3 = { key: 'cow' value: 'yellow' sun: false } type testExpect3 = { key: 'cow' value: 'yellow' sun: false moon: false | undefined } type cases = [ Expect<Equal<AppendToObject<test1, 'home', boolean>, testExpect1>>, Expect<Equal<AppendToObject<test2, 'home', 1>, testExpect2>>, Expect<Equal<AppendToObject<test3, 'moon', false | undefined>, testExpect3>>, // @ts-expect-error Expect<Equal<C, D>> ]
  • 배운 점
    • 첫번째 풀이를 통해
      • A & B 와 A 와 B 에 있는 속성이 동시에 들어있는 것은 타입스크립트 컴파일러가 다르다고 판단한다.
      • type A = { a: string; } type B = { b: string; } type C = A & B; type D = { a: string; b: string; } import type { Equal, Expect } from '@type-challenges/utils' // false Expect<Equal<C, D>>
    • 두번째 풀이를 통해
      • type AppendToObject<T, U, V> = {[index in keyof (T & U)]: index extends keyof T ? T[index] : V}
        이 풀이는 object 가 가지고 있는 기본적인 속성들을 모두 포함합니다.
    • 세번째 풀이를 통해
      • U 는 이미 keyof anyextends 이므로, keyof TU 의 합집합을 순회함
 

529 Absolute

문제: number, string, 혹은 bigint을 받는 Absolute 타입을 만드세요. 출력은 양수 문자열이어야 합니다.
/* _____________ 여기에 코드 입력 _____________ */ type Absolute<T extends number | string | bigint> = `${T}` extends `-${infer Rest}` ? `${Rest}` : `${T}`; type Test = Absolute<-5> /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Absolute<0>, '0'>>, Expect<Equal<Absolute<-0>, '0'>>, Expect<Equal<Absolute<10>, '10'>>, Expect<Equal<Absolute<-5>, '5'>>, Expect<Equal<Absolute<'0'>, '0'>>, Expect<Equal<Absolute<'-0'>, '0'>>, Expect<Equal<Absolute<'10'>, '10'>>, Expect<Equal<Absolute<'-5'>, '5'>>, Expect<Equal<Absolute<-1_000_000n>, '1000000'>>, Expect<Equal<Absolute<9_999n>, '9999'>>, ]
  • 배운 점
    • `${T}` extends `${A}${B}` 형태로 앞 타입을 템플릿 리터럴 형태로도 선처리 할 수 있다.
 

531 String to union

문제: 문자열 인수를 입력받는 String to Union 유형을 구현하세요. 출력은 입력 문자열의 Union type이어야 합니다.
/* _____________ 여기에 코드 입력 _____________ */ type StringToUnion<T extends string, C = never> = T extends `${infer First}${infer Rest}` ? StringToUnion<Rest, C | First> : C; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<StringToUnion<''>, never>>, Expect<Equal<StringToUnion<'t'>, 't'>>, Expect<Equal<StringToUnion<'hello'>, 'h' | 'e' | 'l' | 'l' | 'o'>>, Expect<Equal<StringToUnion<'coronavirus'>, 'c' | 'o' | 'r' | 'o' | 'n' | 'a' | 'v' | 'i' | 'r' | 'u' | 's'>>, ]
  • 배운 점
    • 유니온 타입은 내부 처리로 인해 둘이 같다.
    • Expect<Equal<'h' | 'e' | 'l' | 'o', 'h' | 'e' | 'l' | 'l' | 'o'>>,
 

599 Merge

문제: 두개의 타입을 새로운 타입으로 병합하세요. 두번째 타입의 Key가 첫번째 타입을 덮어씁니다(재정의합니다)
/* _____________ 여기에 코드 입력 _____________ */ type Merge<F, S> = {[index in (keyof F | keyof S)]: index extends keyof S ? S[index] : index extends keyof F ? F[index] : never} /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type Foo = { a: number b: string } type Bar = { b: number c: boolean } type cases = [ Expect<Equal<Merge<Foo, Bar>, { a: number b: number c: boolean }>>, ]
  • 풀이
    • 두 타입의 키를 합쳐 순회 돌면서 첫번째 타입을 두번째 타입의 key 가 덮어 쓰기 때문에 S 를 우선 return

612 KebabCase

문제: camelCase나 PascalCase를 kebab-case 문자열로 수정하세요.
/* _____________ 여기에 코드 입력 _____________ */ type KebabCase<S extends string> = S extends `${infer First}${infer Rest}` ? Rest extends Uncapitalize<Rest> ? `${Uncapitalize<First>}${KebabCase<Rest>}` : `${Uncapitalize<First>}-${KebabCase<Rest>}` : S; type Test = KebabCase<'ABC'>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<KebabCase<'FooBarBaz'>, 'foo-bar-baz'>>, Expect<Equal<KebabCase<'fooBarBaz'>, 'foo-bar-baz'>>, Expect<Equal<KebabCase<'foo-bar'>, 'foo-bar'>>, Expect<Equal<KebabCase<'foo_bar'>, 'foo_bar'>>, Expect<Equal<KebabCase<'Foo-Bar'>, 'foo--bar'>>, Expect<Equal<KebabCase<'ABC'>, 'a-b-c'>>, Expect<Equal<KebabCase<'-'>, '-'>>, Expect<Equal<KebabCase<''>, ''>>, Expect<Equal<KebabCase<'😎'>, '😎'>>, ]
  • 배운 점
    • 처음에는 Lowercase 유틸리티 타입을 사용해서 풀어보려고 했으나 쉽지 않았음
    • Uncapitalize 유틸리티 타입은 문자열의 첫 문자를 소문자로 변경해줌, intrinsic