타입 챌린지 Basic 풀이
type-challenges의 Basic 난이도 문제를 풀어봅니다. 유틸리티 타입을 직접 구현하면서 Mapped Type, Conditional Type, infer 등 TypeScript 타입 시스템의 핵심 도구를 익힐 수 있습니다.
1. Pick
섹션 제목: “1. Pick”주어진 타입에서 특정 프로퍼티만 선택합니다.
interface Todo { title: string; description: string; completed: boolean;}
type MyPick<T, K extends keyof T> = { [key in K]: T[key];};
type TodoPreview = MyPick<Todo, "title" | "completed">;// { title: string; completed: boolean }2. Readonly
섹션 제목: “2. Readonly”모든 프로퍼티를 readonly로 만듭니다.
interface Todo { title: string; description: string;}
type MyReadonly<T extends object> = { readonly [key in keyof T]: T[key];};
const todo: MyReadonly<Todo> = { title: "Hey", description: "foobar",};
todo.title = "Hello"; // Error: cannot reassign a readonly property3. Tuple to Object
섹션 제목: “3. Tuple to Object”튜플의 각 요소를 키이자 값으로 하는 객체 타입을 만듭니다.
const tuple = ["tesla", "model 3", "model X", "model Y"] as const;
export type TupleToObject<T extends readonly any[]> = { [key in T[number]]: key;};
type result = TupleToObject<typeof tuple>;// { tesla: "tesla"; "model 3": "model 3"; ... }튜플 타입의 원형은 readonly any[]입니다. T[number]로 모든 요소의 유니온을 추출합니다.
4. First of Array
섹션 제목: “4. First of Array”배열의 첫 번째 요소 타입을 추출합니다.
type arr1 = ["a", "b", "c"];type arr2 = [3, 2, 1];
type First<T extends any[]> = T[0];
type head1 = First<arr1>; // "a"type head2 = First<arr2>; // 35. Length of Tuple
섹션 제목: “5. Length of Tuple”튜플의 길이를 타입으로 추출합니다.
type tesla = ["tesla", "model 3", "model X", "model Y"];
type Length<T extends any[]> = T["length"];
type teslaLength = Length<tesla>; // 46. Exclude
섹션 제목: “6. Exclude”유니온에서 특정 멤버를 제외합니다.
type MyExclude<T, K extends T> = T extends K ? never : T;
type Result = MyExclude<"a" | "b" | "c", "a">; // "b" | "c"7. Awaited
섹션 제목: “7. Awaited”Promise를 풀어서 내부 타입을 추출합니다. 중첩된 Promise도 처리해야 합니다.
type ExampleType = Promise<string>;
type MyAwaited<T extends Promise<any>> = T extends Promise<infer U> ? U extends Promise<any> ? MyAwaited<U> : U : never;
type Result = MyAwaited<ExampleType>; // stringPromise<Promise<string>>처럼 중첩된 경우에는 재귀를 통해 한 번 더 풀어줘야 합니다.
8. If
섹션 제목: “8. If”불리언 타입에 따라 분기하는 조건부 타입입니다.
type If<T extends boolean, U, V> = T extends true ? U : V;
type A = If<true, "a", "b">; // "a"type B = If<false, "a", "b">; // "b"9. Concat
섹션 제목: “9. Concat”두 튜플을 이어 붙입니다.
type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U];
type Result = Concat<[1, 2, 3], [2]>; // [1, 2, 3, 2]10. Includes
섹션 제목: “10. Includes”튜플에 특정 타입이 포함되어 있는지 확인합니다.
type Includes<T extends any[], U> = { [Key in T[number]]: true;}[U] extends true ? true : false;
type isPillarMen = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], 3>; // false11. Push
섹션 제목: “11. Push”튜플의 끝에 요소를 추가합니다.
type Push<T extends unknown[], U> = [...T, U];
type Result = Push<[1, 2], "3">; // [1, 2, "3"]12. Unshift
섹션 제목: “12. Unshift”튜플의 앞에 요소를 추가합니다.
type Unshift<T extends unknown[], U> = [U, ...T];
type Result = Unshift<[1, 2], 0>; // [0, 1, 2]13. Parameters
섹션 제목: “13. Parameters”함수의 매개변수 타입을 튜플로 추출합니다. infer를 사용해 함수 시그니처에서 매개변수를 추론합니다.
type MyParameters<T extends (...rest: any[]) => any> = T extends ( ...rest: infer R) => any ? R : never;
const foo = (arg1: string, arg2: number): void => {};
type FunctionParamsType = MyParameters<typeof foo>;// [arg1: string, arg2: number]