https://github.com/type-challenges/type-challenges
해당 링크의 타입 챌린지 문제들을 하루에 2문제씩 풀어보면서 기록을 남기기 위함.
easy - 4 pick
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
// 정답
type MyPick<T, K extends keyof T> = { [key in K]: T[key] }
타입스크립트의 Pick<Types, keys>
이라는 유틸리티 타입을 직접 구현해보는 문제. 어려운데 easy라니...
easy - 7 readonly
interface Todo {
title: string
description: string
}
const todo: MyReadonly<Todo> = {
title: "Hey",
description: "foobar"
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
// 정답
type MyReadonly<T> = { readonly [key in keyof T]: T[key] }
이건 조금 해볼만 했던 문제
easy - 11 Tuple to Object
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple> // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
// 정답
type TupleToObject<T extends readonly (string|number)[]> = {
[key in T[number]]: key
}
배열의 특징을 안다면 index
의 type
은 number
이고 이를 활용해야 하는 문제
easy - 14 First of Array
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
// 정답 1
type First<T extends any[]> = T extends [] ? never : T[0]
// 정답 2
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]
ts에서는 이런 식으로 조건문을 사용...
빈배열의 경우 never
를 반환해야하는데 이에 대한 처리가 조금 까다로웠던 문제
easy - 18 length of tuple
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
// 정답
type Length<T extends readonly string[]> = T['length']
이전 문제랑 비슷해서 쉽게 해결할 수 있었던 문제
easy - 43 exclude
type Result = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
// 정답
type MyExclude<T, U> = T extends U ? never : T;
never
를 거의 안 써봐서 어려웠다... never를 정리한 토스트 문서
easy - 189 awaited
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string
// 정답
type MyAwaited<T> = T extends Promise<infer P> ? MyAwaited<P> : T
이런 재귀적인 느낌의 코드, infer 모두 익숙하지 않아... 꽤 많이 어려웠던 문제
해당문서를 통해 조금 더 infer 키워드에 대해 공부할 수 있었다.
easy - 268 if
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
// 정답
type If<C extends boolean, T, F> = C extends true ? T : F
easy - 533 concat
type Result = Concat<[1], [2]> // expected to be [1, 2]
// 정답
type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U]
easy - 3057 push
type Result = Push<[1, 2], '3'> // [1, 2, '3']
// 정답
type Push<T extends unknown[], U> = [...T, U]
easy - 3060 unshift
type Result = Unshift<[1, 2], 0> // [0, 1, 2,]
// 정답
type Unshift<T extends unknown[], U> = [U, ...T]
easy - 3312 parameters
const foo = (arg1: string, arg2: number): void => {}
type FunctionParamsType = MyParameters<typeof foo> // [arg1: string, arg2: number]
// 정답
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never;
typeof 함수
는 T extends (...args: any[]) => any
의 형태를 가진다는 사실을 기억하자!
normal - 2 get return type
const fn = (v: boolean) => {
if (v)
return 1
else
return 2
}
type a = MyReturnType<typeof fn> // should be "1 | 2"
// 정답
type MyReturnType<T extends Function> = T extends (...args: any[]) => infer U ? U : never;
함수의 반환값에 대한 타입을 제네릭 U로 기억하고 이를 반환시키면 되는 문제. 함수의 경우 반환값이 없는 경우도 존재하기 때문에 이 경우 타입은 never
로 대체
infer
자체가 조건부 타입에서만 사용되고 boolean
의 느낌이라고 받아들이면 이해하기 좋을 것 같다.
normal - 3 omit
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyOmit<Todo, 'description' | 'title'>
const todo: TodoPreview = {
completed: false,
}
// 정답
type MyExclude<T, U> = T extends U ? never : T
type MyOmit<T, K extends keyof T> = { [U in MyExclude<keyof T, K>]: T[U] }
normal - 8 Readonly 2
interface Todo {
title: string
description: string
completed: boolean
}
const todo: MyReadonly2<Todo, 'title' | 'description'> = {
title: "Hey",
description: "foobar",
completed: false,
}
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly property
todo.completed = true // OK
// 정답
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [Key in K]: T[Key]
} & {
[Key in Exclude<keyof T, K>]: T[Key]
}
= keyof T 가 어떠한 의미인지 확인 필요
normal - 9 Deep ReadOnly
type X = {
x: {
a: 1
b: 'hi'
}
y: 'hey'
}
type Expected = {
readonly x: {
readonly a: 1
readonly b: 'hi'
}
readonly y: 'hey'
}
type Todo = DeepReadonly<X>
// 정답
type DeepReadonly<T> = {
readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]>;
};
재귀 느낌의 문제. 이 문제에서의 핵심은 원시타입은 그대로 readonly
를 적용하고 객체는 readonly
를 적용하되, 내부에도 다시 readonly
를 적용해야하는 점에 있었다.
keyof T extends never
구문이 가장 중요했는데 이곳에 의하면 known key
를 확인하는 로직이라고 한다. known key
라는 개념이 확 와닫지는 않은데 잘 외워두면 써먹을 곳이 많은 구문인 것 같다.
normal - Tuple to Union
type Arr = ['1', '2', '3']
type Test = TupleToUnion<Arr> // expected to be '1' | '2' | '3'
// 정답
type TupleToUnion<T extends unknown[]> = T[number];
이전에 풀어봤던 문제와 거의 동일한 문제
normal - Last of Array
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
// 정답
type Last<T extends any[]> = T extends [...infer rest, infer last] ? last : never;
normal - Pop
type arr1 = ['a', 'b', 'c', 'd']
type arr2 = [3, 2, 1]
type re1 = Pop<arr1> // expected to be ['a', 'b', 'c']
type re2 = Pop<arr2> // expected to be [3, 2]
// 정답
type Pop<T extends any[]> = T extends [...infer rest, infer last] ? rest : [];
type a = StartsWith<'abc', 'ac'> // expected to be false
type b = StartsWith<'abc', 'ab'> // expected to be true
type c = StartsWith<'abc', 'abcd'> // expected to be false
// 정답
type StartsWith<T extends string, U extends string> = T extends `${U}${infer L}` ? true : false;
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false;
type a = EndsWith<'abc', 'bc'> // expected to be true
type b = EndsWith<'abc', 'abc'> // expected to be true
type c = EndsWith<'abc', 'd'> // expected to be false
// 정답
type EndsWith<T extends string, U extends string> = T extends `${infer L}${U}` ? true : false;
type EndsWith<T extends string, U extends string> = T extends `${string}${U}` ? true : false;
infer keyword 문서 꽤나 많이 어렵,,.
카톡외않돼나요