TS infer 활용하기

이수빈·2025년 1월 2일
0

Typescript

목록 보기
16/17
post-thumbnail

unknown vs never type 차이점

unknown

  • 어떤 타입일지 아직 모른다는 의미를 가지고 있다.

  • 모든 타입의 값을 가질 수 있지만, 타입이 결정되지 않아서 특정 작업을 수행하려면 타입 체크가 필요

  • any와 비슷하지만, 더 안전하게 사용된다. unknown 타입은 직접 사용할 수 없고, 타입을 좁히거나 캐스팅해야만 조작 가능.

let value: unknown;

value = "hello";  // OK
value = 42;       // OK

// 에러: 'unknown' 타입에서는 직접 작업을 할 수 없음
// console.log(value.toUpperCase());

// 타입 좁히기 후 사용
if (typeof value === "string") {
    console.log(value.toUpperCase()); // OK
}

never

  • 절대로 발생하지 않는 값"이라는 의미이다.

  • 어떤 값도 가질 수 없는 타입. 주로 절대 도달할 수 없는 코드나 에러 처리를 나타낼 때 사용됨.

  • 함수가 항상 예외를 던지거나 무한 루프에 들어가는 경우 반환 타입으로 never를 사용.

function error(message: string): never {
    throw new Error(message); // 항상 예외를 던지므로 반환되지 않음
}

function infiniteLoop(): never {
    while (true) {
        // 무한 루프
    }
}

// 타입 체킹에서 사용
type SomeType = "a" | "b";
function checkValue(value: SomeType): string {
    if (value === "a") {
        return "A";
    } else if (value === "b") {
        return "B";
    } else {
        const unreachable: never = value; // 이 코드는 절대 실행되지 않아야 함
        return unreachable; // 타입 오류 방지
    }
}

infer?

  • 조건부 타입에서 타입을 추론하기 위해 사용되는 키워드

  • 어떤 type context내에서 type을 변수처럼 사용한다고 생각하면 쉽다.

  • 조건의 true분기에서 사용한다.

  • 여러가지 활용방법들이 존재한다..


// 배열에서 요소 유형 추출
type ElementType<T> = T extends Array<infer U> ? U : T;


type A = ElementType<number[]>;  // A is number
type B = ElementType<string[]>; // B is string
type C = ElementType<boolean>;  // C is boolean (fallback case)


// 함수의 반환 유형 추출
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Fn = () => string;

type Result = ReturnType<Fn>;  // Result is string

// 첫번째 인수 유형 추출
type FirstArgument<T> = T extends (arg: infer U, ...args: any[]) => any ? U : never;

type Fn = (x: number, y: string) => void;

type Arg = FirstArgument<Fn>;  // Arg is number

infer의 활용방법

배열에서 재귀적으로 배열의 아이템만 추출하기

export type TRemoveArray<T> = T extends Array<infer U> ? TRemoveArray<U> : T;

함수에서 payload, return type 추출하기

- TSPlayGround

  • Awaited Type은 래핑된 Promise를 없앨때 사용하는 타입이다.
type TPromise = Promise<1>;

type ExtractPromise = Awaited<TPromise>; // 1
  • 다음과 같은 예시코드와 requestApi가 있다고 하자.

  • 여기서 response는 객체배열 형태이다. 객체로 왔을때도 가능하다.

const payload = {
    item : '1',
} as const

const arrayResponse = [{
    test : '1'
}] as const;

type TPayload = typeof payload;

type TArrayResponse = typeof arrayResponse;

const requestApi = (payload? : TPayload, signal?: AbortSignal | undefined) => Promise.resolve(arrayResponse);
  • 여기서 첫번째 payload를 추출하는 utility type을 infer와 함깨 작성하면 다음과 같다.

  • 화살표함수에 argument객체는 그냥 사용할 수 없다. => ...rest파라미터 형태로 spread해서 사용해야한다.

(https://velog.io/@ansrjsdn/%EC%99%9C-%ED%99%94%EC%82%B4%ED%91%9C-%ED%95%A8%EC%88%98%EC%97%90%EB%8A%94-arguments%EA%B0%80-%EC%97%86%EC%9D%84%EA%B9%8C)

  • 이렇게되면 args는 파라미터를 담은 배열이 되는데, 이를 destructing 해서 => 첫번째 값을 infer를 통해 type을 추론하도록 만들어주는 형식이다.

  • T는 제너릭으로 일단 함수형태만 올수 있도록 제약을 걸었다.

type ExtractPayload<T extends ((...args: any) => any)> = T extends ((...args: [infer Payload, ...any]) => any) ? Payload : never;

type TExtractedPayload = ExtractPayload<typeof requestApi>;
  • Return Type을 반환하는 Utility type을 작성하면 다음과 같다.

  • requestApi가 Promise를 반환하므로 => Awaited Type을 통해 래핑된 Promise를 제거해줘야한다.

  • 배열은 number indexSignature를 가진다. => 이를 통해 배열의 내부 타입만 추출 가능하다.

type ExtractResponse<T extends ((...args: any) => any)> = Awaited<ReturnType<T>>;

type TExtractedResponse = ExtractResponse<typeof requestApi>[number];
profile
응애 나 애기 개발자

0개의 댓글