在 TypeScript 中推断函数参数 [英] Inferring function parameters in TypeScript

查看:18
本文介绍了在 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');

是否可以让 TypeScript 推断它们?目前 mapped 对象内的函数接受任何参数,但它应该只接受未映射对象中定义的参数.函数名称确实得到了正确推断.

Is it possible to let TypeScript infer them? Currently the functions inside the mapped object accept any parameters, but it should only take the parameters defined in the unmapped object. The function names do get inferred correctly.

推荐答案

如果你使用 (...args: any[]) =>Promise 作为映射类型中的签名,您将丢失所有参数类型信息并返回类型信息.使用条件类型可以实现对您想要做的事情的不完美解决方案.此处描述了这些限制>.

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. An imperfect solution to what you want to do can be achieved using conditional types. The limitations are described here.

该解决方案需要创建一个条件类型,分别处理具有给定数量参数的每个函数.下面的解决方案最多适用于 10 个参数(对于大多数实际情况来说已经足够了)

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)

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'); 

这篇关于在 TypeScript 中推断函数参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆