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 any
의 extends
이므로, keyof T
와 U
의 합집합을 순회함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