简体   繁体   English

在TypeScript中推断函数参数

[英]Inferring function parameters in TypeScript

I'm trying to make a type-safe map function (not the function below), but I'm stuck on getting the function parameters to infer correctly. 我正在尝试创建一个类型安全的映射函数(不是下面的函数),但我仍然坚持让函数参数正确推断。

    export type Mapper<U extends Unmapped> = {
      mapped: Mapped<U>
    };

    export type Unmapped = {
      [name: string]: (...args: any[]) => any
    };

    export type Mapped<U extends Unmapped> = {
      [N in keyof U]: (...args: any[]) => Promise<any>
    };

    const map = <U extends Unmapped>(unmapped: U): Mapper<U> => ({
      mapped: Object.entries(unmapped).reduce(
        (previous, [key, value]) => ({
          ...previous,
          [key]: (...args: any[]) => new Promise((resolve) => resolve(value(...args)))
        }),
        {}
      ) as Mapped<U>
    });

    const mapped = map({ test: (test: number) => test });

    mapped.mapped.test('oh no');

Is it possible to let TypeScript infer them? 是否可以让TypeScript推断它们? Currently the functions inside the mapped object accept any parameters, but it should only take the parameters defined in the unmapped object. 目前, mapped对象内的函数接受任何参数,但它应该只接受未映射对象中定义的参数。 The function names do get inferred correctly. 函数名称可以正确推断。

Can use Parameters and ReturnType generic types to get the specific parameters and return type of the function: 可以使用ParametersReturnType泛型类型来获取函数的特定参数和返回类型:

type Promisified<T extends (...args: any[]) => any> = (...args: Parameters<T>) => Promise<ReturnType<T>>;

export type Mapped<U extends Unmapped> = {
    [N in keyof U]: Promisified<U[N]>
}

If you use (...args: any[]) => Promise<any> as the signature in the mapped type you will loose all parameter type info and return type info. 如果您使用(...args: any[]) => Promise<any>作为映射类型中的签名,您将丢失所有参数类型信息并返回类型信息。 An imperfect solution to what you want to do can be achieved using conditional types. 使用条件类型可以实现对您想要做的事情的不完美解决方案。 The limitations are described here . 这里描述这些限制。

The solution would require the creation of a conditional type that handles each function with a given number of parameters separately. 该解决方案需要创建一个条件类型,该条件类型分别处理具有给定数量参数的每个函数。 The solution below works for up to 10 parameters (more then enough for most practical cases) 下面的解决方案适用于多达10个参数(对于大多数实际情况而言足够多)

export type Mapper<U extends Unmapped> = {
    mapped: Mapped<U>
};

export type Unmapped = {
    [name: string]: (...args: any[]) => any
};

type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;

type Promisified<T extends Function> =
    T extends (...args: any[]) => Promise<any> ? T : (
        T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => infer R ? (
            IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => Promise<R> :
            IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => Promise<R> :
            IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => Promise<R> :
            IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => Promise<R> :
            IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => Promise<R> :
            IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => Promise<R> :
            IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => Promise<R> :
            IsValidArg<C> extends true ? (a: A, b: B, c: C) => Promise<R> :
            IsValidArg<B> extends true ? (a: A, b: B) => Promise<R> :
            IsValidArg<A> extends true ? (a: A) => Promise<R> :
            () => Promise<R>
        ) : never
    );

export type Mapped<U extends Unmapped> = {
    [N in keyof U]: Promisified<U[N]>
}

const map = <U extends Unmapped>(unmapped: U): Mapper<U> => ({
    mapped: Object.entries(unmapped).reduce(
        (previous, [key, value]) => ({
            ...previous,
            [key]: (...args: any[]) => new Promise((resolve) => resolve(value(...args)))
        }),
        {}
    ) as Mapped<U>
});

const mapped = map({ test: (test: number) => test });

mapped.mapped.test('oh no'); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM