본문 바로가기

개발하자

일반 유형을 사용하여 스크립트 래핑 함수를 입력합니다.

반응형

일반 유형을 사용하여 스크립트 래핑 함수를 입력합니다.

Typescript에서 일반 유형을 변경하지 않고 함수를 래핑하려면 어떻게 해야 합니까?

function x() {
  console.log('Original Function');
}

function wrapper<T extends Function>(func: T): T {
  // Typescript compiler error:
  // Type '() => void' is not assignable to type 'T'.
  return () => {
    console.log('Wrapped Function');
    func.call(null);
  }
}

const xWrapped = wrapper(x);
xWrapped(); // logged 'Wrapped Function' & 'Original Function'



래퍼 함수는 함수를 받아들이고 정확히 동일한 형식의 서명 함수를 반환해야 한다.

형식 스크립트는 전달된 함수가 몇 개의 인수를 사용하는지 또는 무엇을 반환하는지 알 방법이 없으며 함수는 0개의 인수를 사용하고 void를 반환한다고 암시적으로 가정합니다.

현재는 기능 서명을 타이프 스크립트에 저장할 수 있는 좋은 방법이 없습니다. 그러나 이 문제를 해결할 수 있는 새로운 버전이 있습니다.

지금은 이렇게 생긴 일반적인 포장지를 만들 수 있습니다.

function wrapper<T extends (...args:any[])=>any>(func: T): T {
  return <T>((...args:any[]) => {
        console.log('Wrapped Function');
        return func(...args);
    });
}

Variadic type에 대한 제안으로 이 함수는 다음과 같이 작성될 수 있습니다.

function wrapper<...TArgs,TRet>(func:(...args:...TARGS)=>TRet) {
    return (...args:...TARGS) => {
        console.log("Wrapped function");
        return func(...args);
    }
}

여기서 중요한 차이점은 위의 솔루션이 컴파일러에게 반환 변수가 입력 변수와 같은 유형이라고 말해야 했던 캐스트가 없다는 것이다. 그러나 변수 형식에서는 변수 형식 자체를 일반적으로 입력할 수 있습니다. (변수 형식은 현재 typescript에 없으며 doget가 포함된 경우 위의 코드에 구문 오류가 있을 가능성이 있습니다.)

구현된 제안과 함께 래퍼의 작업 코드는 다음과 같습니다.

type InferArgs<T> = T extends (...t: [...infer Arg]) => any ? Arg : never;
type InferReturn<T> = T extends (...t: [...infer Arg]) => infer Res ? Res : never;

function getWrapper<TFunc extends (...args: any[]) => any>(func: TFunc)
    : (...args: InferArgs<TFunc>) => InferReturn<TFunc> {

    return (...args: InferArgs<TFunc>) => {
        // something before

        try {
            return func(...args);
        } finally {

            // something after;
        }
    };
}

예시와 함께 Variadic Tupples의 사용에 관한 몇 가지 짧지만 유용한 정보는 여기에서 찾을 수 있습니다: .




변수 유형에 의존하지 않고 인수를 보존하고 내부 함수의 유형을 반환하는 대안이 있습니다.

function x(message: string): void {
    console.log(`inner ${message}`);
}

export function wrapper<Args extends any[], Return>(
    operation: (...operationParameters: Args) => Return, 
    ...parameters: Args
): Return {
    console.log(`outer `);

    return operation(...parameters);
}

x("abc");
wrapper(x, "xyz");

// output:
//
// inner abc
// outer
// inner xyz

와 함께 호출되면 TS 컴파일러는 형식을 로 유추합니다.

호출하려고 하면 아름다운 유형의 안전과 함께 실패합니다.




다음과 같은 간단한 솔루션을 선호합니다.


function wrapFunction<TArgs extends any[], TReturn>(
  targetFunction: (...parameters: TArgs) => TReturn,
): (...parameters: TArgs) => TReturn {
  return (...parameters: TArgs) => {
    console.log(`Hello, what is your name?`);
    return targetFunction(...parameters);
  };
}


// --------------- Example
const someFunction = (name: string) => {
    console.log(`Hey! My name is ${name}.`);
}

const wrappedFunction = wrapFunction(someFunction);
wrappedFunction("Fábio");

산출량

[LOG]: "Hello, what is your name?" 
[LOG]: "Hey! My name is Fábio." 

또는 보다 일반적인 것을 원하는 경우:

export function wrapFunction<TArgs extends any[], TReturn>(
  targetFunction: (...parameters: TArgs) => TReturn,
  wrapperFunction: (...parameters: TArgs) => void,
): (...parameters: TArgs) => TReturn {
  return (...parameters: TArgs) => {
    wrapperFunction(...parameters);
    return targetFunction(...parameters);
  };
}



// --------------- Example
const someFunction = (name: string) => {
    console.log(`Hey! My name is ${name}.`);
}

const wrapperFunction = (name: string) => {
    console.log(`The wrapper - Hey! My name is ${name}.`);
}

const wrappedFunction = wrapFunction(someFunction, wrapperFunction);
wrappedFunction("Fábio");

산출량

[LOG]: "The wrapper - Hey! My name is Fábio." 
[LOG]: "Hey! My name is Fábio."



여기 제가 찾은 다른 방법이 있습니다. 포장된 기능의 제네릭까지도 유지합니다.

const newFunction = ((one, two) => {
    // something before
    const result = oldFunction(one, two)
    // something after
    return result
}) as typeof oldFunction

반응형