【问题标题】:Inferring function parameters in TypeScript在 TypeScript 中推断函数参数
【发布时间】:2018-11-19 05:55:02
【问题描述】:

我正在尝试创建一个类型安全的映射函数(不是下面的函数),但我一直坚持让函数参数正确推断。

    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 对象内的函数接受任何参数,但它应该只接受未映射对象中定义的参数。确实可以正确推断出函数名称。

【问题讨论】:

    标签: typescript typescript-typings


    【解决方案1】:

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

    解决方案需要创建一个条件类型,该类型可以分别处理具有给定数量参数的每个函数。下面的解决方案适用于多达 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'); 
    

    【讨论】:

      【解决方案2】:

      可以使用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]>
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-15
        • 2019-04-20
        • 2022-01-14
        • 2021-12-20
        • 2016-11-01
        • 2021-09-08
        相关资源
        最近更新 更多