[TS] Type-Challenges 스터디 4주차
![[TS] Type-Challenges 스터디 4주차](/_next/image?url=https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Fhayou%2Fpost%2F2e30c95f-940e-46eb-869f-37e27f6232ed%2Fimage.jpg&w=3840&q=75)
[Medium] 15. Last of Array
View on GitHub: https://tsch.js.org/15
문제
배열 T를 사용하고 마지막 요소를 반환하는 제네릭 Last<T>를 구현합니다.
예시
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 LastElement]
? LastElement
: never;
설명
- 898-includes 문제 참고
- 스프레드 연산자를 이용해 배열을 분해하고, 마지막 요소를 반환
[Medium] 16. Pop
View on GitHub: https://tsch.js.org/16
문제
배열 T를 사용해 마지막 요소를 제외한 배열을 반환하는 제네릭 Pop<T>를 구현합니다.
예시
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 : [];
설명
- 15번 문제와 동일하게 스프레드 연산자를 이용해 배열을 분해하고, 마지막 요소를 제외한 배열을 반환
[Medium] 20. Promise.all
View on GitHub: https://tsch.js.org/20
문제
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.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});
// expected to be `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const);
정답
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;
declare function PromiseAll<T extends any[]>(
values: readonly [...T]
): Promise<{
[K in keyof T]: Awaited<T[K]>;
}>;
에러 케이스
declare function PromiseAll<T extends any[]>(
values: T
): Promise<{ [K in keyof T]: T[K] extends Promise<infer U> ? U : T[K] }>;
설명
PromiseAll함수는 배열의 각 요소를Promise로 감싸서 반환하는 함수- 우선 테스트케이스를 보면 readonly 배열(튜플)과 일반 배열이 모두 존재함
- 따라서 최초
T는 any[]로 받고, 이를 튜플로(readonly)로 변환하기 위해readonly [...T]를 사용 - 이후 배열의 각 요소를
Promise로 감싸서 반환하기 위해Promise<{ [K in keyof T]: Awaited<T[K]>; }>를 사용 { [K in keyof T]: Awaited<T[K]> }는 배열의 각 요소를 순회하며 키를 추출하고, 각 요소를Awaited<T[K]>로 변환하여 반환Awaited<T[K]>는T[K]가Promise라면Promise의 내부 타입을 추출하고, 그렇지 않다면T[K]를 반환
추가 질문
values에 readonly [...T]가 가능한 이유는?
- 기존
T extends any[]에서T는 배열이지만 튜플이 될 수도 있고, 일반 배열이 될 수도 있음 - 따라서
readonly [...T]를 사용하여 튜플로 변환
declare function Example<T extends any[]>(values: readonly [...T]): T;
declare function Example2<T extends any[]>(values: T): T;
const test1 = Example([1, 2, 3]); // T = [number, number, number] (튜플)
const test2 = Example2([1, 2, 3]); // T = number[] (일반 배열)
- TypeScript는
readonly [...T]에 맞춰T를 튜플로 유추하려고 시도함 values가 일반 배열(number[])이면 TypeScript는 가능한 한readonly속성을 유지하려고 함- 결과적으로
T는[number, number, number]처럼 튜플로 추론됨 readonly옵션을 붙여주지 않으면Example2처럼 일반 배열로 추론됨
왜 Awaited가 필요한가?
- Awaited 대신
T[K] extends Promise<infer U> ? U : T[K]를 사용하면 오류 발생 - Typescript에서 조건부 타입(
extends)를 사용할 때,T가 유니온(A | B)이면 각각 개별적으로 평가한 후, 결과를 다시 유니온으로 합침 - 이 때,
T[K]자체가 유니온 타입이기 때문에(이 문제에서는number | Promise<number>),T[K]가 리턴될 경우number | Promise<number>로 추론되어 오류 발생 - 따라서
Awaited<T[K]>를 사용하여T[K]가 리턴될 경우number로 추론되도록 함
Reference
[Medium] 62. Type Lookup
View on GitHub: https://tsch.js.org/62
문제
때때로 유니온 타입의 특정 속성을 기준으로 조회할 수도 있습니다.
이 챌린지에서는 유니온 타입 Cat | Dog에서 공통으로 사용하는 type 필드를 기준으로 해당하는 타입을 얻고자 합니다. 다시 말해서, 다음 예시에서는 LookUp<Cat | Dog, 'dog'>으로 Dog 타입을, LookUp<Cat | Dog, 'cat'>으로 Cat 타입을 얻을 수 있습니다.
interface Cat {
type: "cat";
breeds: "Abyssinian" | "Shorthair" | "Curl" | "Bengal";
}
interface Dog {
type: "dog";
breeds: "Hound" | "Brittany" | "Bulldog" | "Boxer";
color: "brown" | "white" | "black";
}
type MyDogType = LookUp<Cat | Dog, "dog">; // 기대되는 결과는 `Dog`입니다.
정답
type LookUp<U, T> = U extends { type: T } ? U : never;
설명
- 유니온 타입으로 주어지는
U를 분배법칙을 이용해 타입 조회 U가{ type: T }형태를 포함하는지 확인- 포함한다면
U를 반환 - 포함하지 않는다면
never반환
[Medium] 106. Trim Left
View on GitHub: https://tsch.js.org/106
문제
정확한 문자열 타입이고 시작 부분의 공백이 제거된 새 문자열을 반환하는 TrimLeft<T>를 구현하십시오.
type trimed = TrimLeft<" Hello World ">; // expected to be 'Hello World '
정답
type TrimLeft<S extends string> = S extends `${" " | "\n" | "\t"}${infer Rest}`
? TrimLeft<Rest>
: S;
설명
- 문자열
S가" " | "\n" | "\t"로 시작하는지 확인 - 시작하면 문자열
S에서 시작 부분의 공백을 제거한 새 문자열(Rest)을 반환 - 시작하지 않으면 주어진 문자열
S를 그대로 반환
[Medium] 108. Trim
View on GitHub: https://tsch.js.org/108
문제
정확한 문자열 타입이고 시작 부분의 공백이 제거된 새 문자열을 반환하는 Trim<T>를 구현하십시오.
type trimmed = Trim<" Hello World ">; // expected to be 'Hello World'
정답
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> = TrimRight<TrimLeft<S>>;
설명
- 106번
TrimLeft<S>활용해 문자열S에서 시작 부분의 공백을 제거한 새 문자열을 반환 - 같은 원리로
TrimRight<S>를 정의해 문자열S에서 끝 부분의 공백을 제거한 새 문자열을 반환 Trim<S>:TrimLeft<S>와TrimRight<S>를 조합하여 문자열S에서 양쪽 끝의 공백을 제거한 새 문자열을 반환
comments
loading…