icon

메티의 블로그

4주차 타입스크립트 스터디

4주차 타입스크립트 스터디

Tags
TypeScript
날짜
Jan 31, 2025
상태
공개

15 Last of Array

문제: 배열 T를 사용하고 마지막 요소를 반환하는 제네릭 Last<T>를 구현합니다.
/* _____________ 여기에 코드 입력 _____________ */ type Last<T extends any[]> = T extends [...infer _, infer Tail] ? Tail : never /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Last<[]>, never>>, Expect<Equal<Last<[2]>, 2>>, Expect<Equal<Last<[3, 2, 1]>, 1>>, Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>, ]
  • 배운 점
    • infer 를 배열 내에서도 사용할 수 있다.
 

16 Pop

문제: 배열 T를 사용해 마지막 요소를 제외한 배열을 반환하는 제네릭 Pop<T>를 구현합니다.
/* _____________ 여기에 코드 입력 _____________ */ type Pop<T extends any[]> = T['length'] extends 0 ? [] : T extends [...infer Rest, infer _] ? Rest : never /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Pop<[3, 2, 1]>, [3, 2]>>, Expect<Equal<Pop<['a', 'b', 'c', 'd']>, ['a', 'b', 'c']>>, Expect<Equal<Pop<[]>, []>>, ]
  • 배운 점
    • infer 를 rest 연산과 같이 사용할 수 있다
  • 풀이
    • 빈 배열을 위해 T[’length’] extends 0 사용
 

20 Promise.all

문제: Type the function PromiseAll that accepts an array of PromiseLike objects, the returning value should be Promise<T> where T is the resolved result array.
/* _____________ 여기에 코드 입력 _____________ */ // declare function PromiseAll<T extends any[]>(values: T): Promise<{[index in keyof T]: T[index] extends readonly [...infer Const] ? Const : T[index] extends Promise<infer Inner> ? Inner : T[index]}>; // Promise 를 꺼내는 것은 Awaited 로 declare function PromiseAll<T extends any[]>(values: T): Promise<{[index in keyof T]: T[index] extends readonly [...infer Const] ? Const : Awaited<T[index]>}>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' const promiseAllTest1 = PromiseAll([1, 2, 3] as const) const promiseAllTest2 = PromiseAll([1, 2, Promise.resolve(3)] as const) const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)]) const promiseAllTest4 = PromiseAll<Array<number | Promise<number>>>([1, 2, 3]) const promiseAllTest5 = PromiseAll([1, 2, 3]) type cases = [ Expect<Equal<typeof promiseAllTest1, Promise<[1, 2, 3]>>>, Expect<Equal<typeof promiseAllTest2, Promise<[1, 2, number]>>>, Expect<Equal<typeof promiseAllTest3, Promise<[number, number, number]>>>, Expect<Equal<typeof promiseAllTest4, Promise<number[]>>>, Expect<Equal<typeof promiseAllTest5, Promise<[number, number, number]>>>, ]
  • 배운 점
    • interface PromiseLike<T> { /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. * @param onrejected The callback to execute when the Promise is rejected. * @returns A Promise for the completion of which ever callback is executed. */ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>; } /** * Represents the completion of an asynchronous operation */ interface Promise<T> { /** * Attaches callbacks for the resolution and/or rejection of the Promise. * @param onfulfilled The callback to execute when the Promise is resolved. * @param onrejected The callback to execute when the Promise is rejected. * @returns A Promise for the completion of which ever callback is executed. */ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>; /** * Attaches a callback for only the rejection of the Promise. * @param onrejected The callback to execute when the Promise is rejected. * @returns A Promise for the completion of the callback. */ catch<TResult = never>(onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null): Promise<T | TResult>; } /** * Recursively unwraps the "awaited type" of a type. Non-promise "thenables" should resolve to `never`. This emulates the behavior of `await`. */ type Awaited<T> = T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode T extends object & { then(onfulfilled: infer F, ...args: infer _): any; } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument Awaited<V> : // recursively unwrap the value never : // the argument to `then` was not callable T; // non-object or non-thenable
    • infer 를 제네릭 선언 안 쪽에서도 사용할 수 있다.
    • infer 를 함수 args 타입 선언에서도 사용할 수 있다.
  • 풀이
    • as const 처리를 위해 readonly 인 경우 그 값을 그대로 사용
    • 아니면 Awaited 로 꺼냄, Promise 내부에 Promise 가 또 있을 수 있으므로 recursive 하게 구현 (Awaited 는 recursive 하게 구현 되어있음)
 

62 Type Lookup

문제: 이 챌린지에서는 유니온 타입 Cat | Dog에서 공통으로 사용하는 type 필드를 기준으로 해당하는 타입을 얻고자 합니다. 다시 말해서, 다음 예시에서는 LookUp<Cat | Dog, 'dog'>으로 Dog 타입을, LookUp<Cat | Dog, 'cat'>으로 Cat 타입을 얻을 수 있습니다.
/* _____________ 여기에 코드 입력 _____________ */ // type LookUp<U extends {type: string}, T> = U['type'] extends T ? U : never; type LookUp<U, T> = U extends { type: T } ? U : never; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' interface Cat { type: 'cat' breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal' } interface Dog { type: 'dog' breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer' color: 'brown' | 'white' | 'black' } type Animal = Cat | Dog type cases = [ Expect<Equal<LookUp<Animal, 'dog'>, Dog>>, Expect<Equal<LookUp<Animal, 'cat'>, Cat>>, ]
  • 배운 점
    • 직접 접근하게 되면 리터럴 타입으로 (U[’type’])찍힌다.
 

106 Trim Left

문제: 정확한 문자열 타입이고 시작 부분의 공백이 제거된 새 문자열을 반환하는 TrimLeft<T>를 구현하십시오.
/* _____________ 여기에 코드 입력 _____________ */ // infer 로 재지정 기능 활용 type TrimLeft<S extends string> = S extends `${" "|"\n"|"\t"}${infer Rest}` ? TrimLeft<Rest> : S; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<TrimLeft<'str'>, 'str'>>, Expect<Equal<TrimLeft<' str'>, 'str'>>, Expect<Equal<TrimLeft<' str'>, 'str'>>, Expect<Equal<TrimLeft<' str '>, 'str '>>, Expect<Equal<TrimLeft<' \n\t foo bar '>, 'foo bar '>>, Expect<Equal<TrimLeft<''>, ''>>, Expect<Equal<TrimLeft<' \n\t'>, ''>>, ]
  • 배운 점
    • infer 를 통해서 문자열 타입 재정의를 할 수 있다.
 

108 Trim

문제: 정확한 문자열 타입이고 양쪽 끝의 공백이 제거된 새 문자열을 반환하는 Trim<T>를 구현하십시오.
/* _____________ 여기에 코드 입력 _____________ */ type TrimLeft<S extends string> = S extends `${" "|"\n"|"\t"}${infer Rest}` ? TrimLeft<Rest> : S; type TrimRight<S extends string> = S extends `${infer Rest}${" "|"\n"|"\t"}` ? TrimRight<Rest> : S; type Trim<S extends string> = TrimLeft<TrimRight<S>>; /* _____________ 테스트 케이스 _____________ */ import type { Equal, Expect } from '@type-challenges/utils' type cases = [ Expect<Equal<Trim<'str'>, 'str'>>, Expect<Equal<Trim<' str'>, 'str'>>, Expect<Equal<Trim<' str'>, 'str'>>, Expect<Equal<Trim<'str '>, 'str'>>, Expect<Equal<Trim<' str '>, 'str'>>, Expect<Equal<Trim<' \n\t foo bar \t'>, 'foo bar'>>, Expect<Equal<Trim<''>, ''>>, Expect<Equal<Trim<' \n\t '>, ''>>, ]