Typescript - 지연 평가

00_8_3·2023년 4월 9일
0

typescript

목록 보기
4/8

지연 평가

지연평가란 필요한 시점까지 타입 계산을 미루어 컴파일 시간을 줄이고, 불필요한 타입 계산을 피하는 기능입니다.
이를 통해 타입스크립트 컴파일러는 효율적으로 작동하며, 성능 향상을 이룰 수 있습니다.

예시 1

function test<T extends string | number>(x: T): T extends string ? string : number {
    return x // 에러 발생
}

위 코드의 경우 컴파일러가 x가 갖는 타입이 무엇인지 모르기 때문에 에러를 발생시킵니다.

  • 반환 타입이 T extends string ? string : number 이대로 받아들임.

해결방법

타입 단언

function test<T extends string | number>(x: T): T extends string ? string : number {
    if(typeof x === "string") {
        return x as any
    }
    return x as any
}

const a = test(1) // number
const b = test("Asdf") // string
const c = test(true) // 에러

함수 오버로딩

function test(x: string): string;
function test(x: number): number;
function test<T extends string | number>(x: T): string | number {
  return x;
}

test("Asdf");
test(1);
test(true); // 오류

튜플화 (지연 평가 해제)

function Test<T extends [T] extends [string] ? string : number>(
  x: T
): [T] extends [string] ? string : number {
  return x;
}

Test("asdf");
Test(1);
Test(true); // 에러

[] 대괄호로 감싸는 경우 분산성을 방지한다.

왜?

  • 질문
제네릭을 [] 대괄호로 감싸는 경우 분산성을 방지하는데
분산을 방지하는 것이 지연 평가를 막는 이유가 뭐냐
  • gpt
제네릭 타입을 대괄호([])로 감싸는 것이 지연 평가를 막는 이유는, 대괄호를 사용하여 '튜플'이라는 새로운 타입을 만들기 때문입니다.
이렇게 하면 타입스크립트 컴파일러가 분산 조건부 타입(distributive conditional type)을 적용하지 않게 됩니다.

타입스크립트의 분산 조건부 타입은 기본적으로 조건부 타입에 유니온 타입이 있는 경우, 각 유니온 타입의 멤버에 대해 조건부 타입을 개별적으로 적용합니다. 
예를 들어 T extends string ? string : number에서 T가 string | number인 경우, 컴파일러는 두 가지 경우를 모두 고려하게 됩니다.

하지만 튜플 타입을 사용하여 제네릭 타입을 감싸면, 분산 조건부 타입을 방지할 수 있습니다. 
이렇게 되면 컴파일러는 튜플 타입을 전체적으로 평가하게 됩니다. 
예를 들어 [T] extends [string] ? string : number에서 T가 string | number인 경우, 컴파일러는 [string | number] 튜플을 전체적으로 평가합니다.

이러한 전체적인 평가 덕분에 조건부 타입이 지연 평가되지 않습니다. 
결국 컴파일러가 반환 타입을 즉시 결정할 수 있게 되며, 이는 지연 평가를 방지하는데 도움이 됩니다.

대괄호로 제네릭을 감싸는 것은 다소 까다로운 기법이므로, 코드를 이해하고 유지하기 쉽도록 간결한 형태를 사용하는 것이 좋습니다. 
그러나 경우에 따라 이 기법을 사용하여 지연 평가를 방지하고 코드의 동작을 제어할 수 있습니다.
  • 질문2
조건부 타입인 경우에만 지연평가를 하는 것이기 때문에
조건부 타입에서 벗어나서 지연평가가 안된다는 것인가?
  • gpt2
네, 맞습니다. 조건부 타입은 타입 추론 과정에서 런타임에 타입을 결정하려는 것이 아니라, 컴파일 시점에 타입을 결정하려고 시도합니다. 
이러한 특성 때문에 조건부 타입에서 지연 평가가 발생할 수 있습니다.

조건부 타입에서 지연 평가가 발생하는 경우는 주로 제네릭 매개변수를 사용하는 상황입니다. 
제네릭 매개변수는 컴파일 시점에 정확한 타입이 결정되지 않을 수 있으므로, 컴파일러는 이러한 상황에서 조건부 타입의 결론을 지연 평가합니다.

조건부 타입에서 벗어나면 지연 평가가 발생하지 않는 이유는, 
타입스크립트 컴파일러가 컴파일 시점에 타입을 즉시 결정할 수 있기 때문입니다. 
예를 들어, 명시적인 타입 주석이나 함수 오버로드를 사용하는 경우에는 컴파일러가 타입을 즉시 확인하고 평가할 수 있습니다. 
이렇게 명확한 타입 정보를 제공하면 지연 평가를 방지할 수 있습니다.

예시 2

typia 라이브러리를 사용한 validation을 구현할 때 위와 비슷한 지연 평가 에러가 발생한다.

코드

type Test = {
    id: number
}

const test = {
    id: 1
}

const main = () => {
  // const rv: typia.IValidation<Test>
    const rv = typia.validateEquals<Test>(test)

    if(!rv.success){
        return
    }
  // const data: Test
    const data = rv.data
}

typia.validateEquals 함수 파라미터가 제네릭 타입을 만족하는지 검증하는 코드입니다.
rvdata 변수에 마우스를 올리면 타입 추론이 잘되는 것을 볼수 있습니다.

위의 검증하는 코드를 여러 곳에서 사용 할수 있게 함수로 감싸 보자.

코드 2

const validBody = <T>(x: T) => {
    const rv = typia.validateEquals<T>(x)

    if(!rv.success){
        throw new Error()
    }

    const data = rv.data
    return data
}

const main = () => {
    const data = validBody(test)

    console.log("data :", data)
}

위 코드의 data에 마우스를 올리면 타입 추론이 잘되는 것을 볼수 있지만
빌드를 하면 에러가 발생한다.

Error: Error on typia.validateEquals(): non-specified generic argument.
typia 코드

타입스크립트 AST를 만들고 넘겨주어야 하는데

  • transformers\internal\GenericTransformer.ts
            const node: ts.TypeNode = expression.typeArguments[0];
            const type: ts.Type = project.checker.getTypeFromTypeNode(node);

            if (type.isTypeParameter())

typia 코드를 확인하면 validBody AST가 type.isTypeParameter()를 통과해야 하는데
컴파일 타임에 제네릭 T의 구체적인 타입을 모르기 때문에 검사를 통과할 수 없어 에러를 발생 시킵니다.

런타임으로 미루기

type Test = {
    id: number
}

const test = {
    id: 1
}

const validateBody = <T>(closure: (input: unknown) => IValidation<T>) => (body: unknown) => { 
    const result = closure(body)
    if(!result.success){
        // 에러
        return
    }

    return result.data
}

const main = () => {
    const rv = validateBody(createValidateEquals<Test>())(test)
}

0개의 댓글