유틸리티 타입

type MyReadonly<T> = {
  readonly [S in keyof T]: T[S]
}
type MyPick<T, K extends keyof T> = {
  [S in K]: T[S]
}
type MyExclude<T, U> = T extends U ? never : T;
type MyOmit<T, K extends keyof T> = {
  [S in keyof T as S extends K ? never : S] : T[S]
}
type MyParameters<T extends (...args: any[]) => any> = T extends (...args:infer A)=>any? A: never
type MyReturnType<T> = T extends (...args:any)=> infer R ? R:never
type Intersection<T> = {
  [S in keyof T]: T[S]
}

type PartialByKeys<T, K extends keyof T = keyof T> = Intersection<{
  [S in keyof T as S extends K ? S : never]?:T[S]
} &{
  [S in keyof T as S extends K ? never: S] : T[S]
}>
type InterSection<T>={
  [S in keyof T]: T[S]
}

type RequiredByKeys<T, K extends keyof T = keyof T> = InterSection<{
  [S in keyof T as S extends K ? S:never]-?:T[S]
}&{
  [S in keyof T as S extends K ? never : S]:T[S]
}>

고차함수 분석하기

interface Array<T>{
  myForEach(cb:(v:T,i:number,a:T[])=>void):void,
  myMap<U>(cb:(v:T,i:number,a:T[])=>U):U[],
  myFilter<S extends T>(cb:(v:T,i:number,a:T[])=> boolean):S[];
  // 틀림
  myReducer<S>(cb:(a:S,c:T,i:number,arr:T[])=>S,init?:S):S;

}

오답노트

  myFilter:<S extends T>(cb:(v:T,i:number,a:T[])=> unknown)=>S[],

callback의 return이 flasly가 아니면 해당 T를 return하므로 unkown 이라고 생각하였습니다. 하지만 하나 빼먹은것이 있었는데요.

바로 타입 좁히기를 사용할 시 입니다.

const result = [1,"2",3,"4"].myFilter((v,i,a): v is string =>{return typeof v ==="string"});

다음과 같은 타입을 고려할 때 result의 값으로는 string이 와야 합니다. 제가 작성한 코드에서는 이를 추론하지 않고 있습니다.

// 추가
myFilter:<S extends T>(cb:(v:T,i:number,a:T[])=> v is S)=>S[],

다음 타입도 추가해 주어야 합니다.

이렇게 하면 완성이지만 불필요한 타입을 하나 줄일 수 있습니다.

myFilter:<S extends T>(cb:(v:T,i:number,a:T[])=> unknown)=>S[],

이 타입에서 S 제네릭을 없앨 수 있는데요. 이 경우에는 filter가 타입을 바꾸는 것이 아닌 속성의 개수만 변하기 때문에 기존 T타입과 똑같습니다.

결론

myFilter:<S extends T>(cb:(v:T,i:number,a:T[])=> v is S)=>S[],
myFilter:(cb:(v:T,i:number,a:T[])=> unknown)=>T[],

3.10 Promise Await 분석하기

promise는 js의 구현체이기 때문에 타입만 따로 delcare를 이용하여 정의하면 됩니다.

interface PromiseConstructor {
    readonly prototype: Promise<any>;
    new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
    all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;
    race<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;
    reject<T = never>(reason?: any): Promise<T>;
    resolve(): Promise<void>;
    resolve<T>(value: T): Promise<Awaited<T>>;
    resolve<T>(value: T | PromiseLike<T>): Promise<Awaited<T>>;
}

declare var Promise: PromiseConstructor;

여기서 Awaited라는 타입을 볼 수 있는데요

Promise의 return 타입을 꺼내오기 위해서 존재합니다.

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

하나씩 살펴 보겠습니다. 먼저 첫줄은 null과 undefined면 그 타입을 그대로 return해줍니다.

T extends object & { then(onfulfilled: infer F, ...args: infer _): any; }

다음을 보겠습니다. 다음 타입은 Promise 인스턴스 타입을 extends하는가에 대한 여부입니다.

Promise 인스턴스 타입과 Promise 객체는 다릅니다. Promise 인스턴스 타입은 new Promise()나 Promise.resolve()의 반환값을 의미합니다.

즉 이 인스턴스 타입에는 then이라는 매서드가 있기 때문에 해당 타입을 만족합니다.

F extends ((value: infer V, ...args: infer _) => any)

다음은 then의 onfulfilled로 추론된 F타입에 대한 조건문인데요. 해당 F는 다음과 같이 callback을 받고 있는데 저희가 봐야 할것은 이 callback의 타입 자체에 대해서 접근을하고 return을 해주어야 하기 떄문에 한단계 더 infer를 작성한 것입니다.

Promise.resolve('hi').then((data)=>{data})

따라서 Await 내부에 Promise가 들어가면 Await가 되는걸 알 수 있습니다.

results matching ""

    No results matching ""