【问题标题】:Get argument types for function / class constructor获取函数/类构造函数的参数类型
【发布时间】:2017-09-14 20:15:50
【问题描述】:

我正在尝试做一些我不确定在 TypeScript 中是否可行的事情:从函数中推断参数类型/返回类型。

例如:

function foo(a: string, b: number) {
  return `${a}, ${b}`;
}

type typeA = <insert magic here> foo; // Somehow, typeA should be string;
type typeB = <insert magic here> foo; // Somehow, typeB should be number;

我的用例是尝试创建一个包含构造函数和参数的配置对象。

例如:

interface IConfigObject<T> {
    // Need a way to compute type U based off of T.
    TypeConstructor: new(a: U): T;
    constructorOptions: U;
}

// In an ideal world, could infer all of this from TypeConstructor

class fizz {
    constructor(a: number) {}
}

const configObj : IConfigObj = {
    TypeConstructor: fizz;
    constructorOptions: 13; // This should be fine
}

const configObj2 : IConfigObj = {
    TypeConstructor: fizz;
    constructorOptions: 'buzz'; // Should be a type error, since fizz takes in a number
}

【问题讨论】:

    标签: typescript types constructor


    【解决方案1】:

    Typescript 现在具有 ConstructorParameters 内置函数,类似于 Parameters 内置函数。确保传递的是类类型,而不是实例:

    ConstructorParameters<typeof SomeClass>
    

    ConstructorParameter Official Doc

    Parameters Official Doc

    【讨论】:

    • 效果很好!如果 SomeClass 的构造函数将对象作为第一个参数,则获取对象类型的语法为ConstructorParameters&lt;typeof SomeClass&gt;[0]
    • 非常感谢,我喜欢 TypeScript。
    • 但是我们在SomeClass 上有一个通用参数呢?我试过ConstructorParameters&lt;typeof SomeClass&lt;T&gt;&gt;,但发现它的语法无效。
    【解决方案2】:

    使用 TypeScript 2.8,您可以使用新的 extends 关键字:

    type FirstArgument<T> = T extends (arg1: infer U, ...args: any[]) => any ? U : any;
    type SecondArgument<T> = T extends (arg1: any, arg2: infer U, ...args: any[]) => any ? U : any;
    
    let arg1: FirstArgument<typeof foo>; // string;
    let arg2: SecondArgument<typeof foo>; // number;
    let ret: ReturnType<typeof foo>; // string;
    

    【讨论】:

    • 有没有办法从重载函数中提取参数类型?
    【解决方案3】:

    Typescript 2.8 添加了conditional types with type inference

    Typescript 3.0 添加了rest-elements-in-tuple-types,因此您现在可以获取Array 类型中的所有参数。

    type ArgumentsType<T extends (...args: any[]) => any> = T extends (...args: infer A) => any ? A : never;
    
    type Func = (a: number, b: string) => boolean;
    type Args = ArgumentsType<Func> // type Args = [number, string];
    type Ret = ReturnType<Func> // type Ret = boolean;
    

    你可以这样使用它:

    const func = (...args: Args): Ret => { // type the rest parameters and return type
      const [a, b] = args; // spread the arguments into their names
      console.log(a, b); // use the arguments like normal
      return true;
    };
    
    // Above is equivalent to:
    const func: Func = (a, b) => {
      console.log(a, b);
      return true;
    }
    

    【讨论】:

      【解决方案4】:

      我将为提取构造函数参数类型的用例提供更直接的答案。

      type GetConstructorArgs<T> = T extends new (...args: infer U) => any ? U : never
      
      class Foo {
          constructor(foo: string, bar: number){
              //
          }
      }
      
      type FooConstructorArgs = GetConstructorArgs<typeof Foo> // [string, number]
      

      【讨论】:

        【解决方案5】:

        这个方法怎么样:

        interface IConfigObject<T, U> {
            TypeConstructor: new(a: U) => T;
            constructorOptions: U;
        }
        
        class fizz {
            constructor(a: number) {}
        }
        
        function createConfig<U, T>(cls: { new (arg: U): T }, arg: U): IConfigObject<T, U> {
            return {
                TypeConstructor: cls,
                constructorOptions: arg
            }
        }
        
        const configObj = createConfig(fizz, 3); // ok
        const configObj2 = createConfig(fizz, "str"); // error
        

        (code in playground)


        编辑

        你可以有一个索引类型变量:

        const configs: { [name: string]: IConfigObject<any, any> } = {
            config1: createConfig(fizz, 3),
            config2: createConfig(fizz, "str"), // error
            config3: createConfig(buzz, "str")
        }
        

        【讨论】:

        • 这绝对可以。有没有办法让它作为索引签名工作?例如: const configs : SomeType = { config1: { TypeConstructor: fizz, constructorOptions: 3, }, config2: { TypeConstructor: fizz, constructorOptions: 'str', // 应该抛出错误 } config3: { TypeConstructor: buzz, constructorOptions: 'str', } }
        • 当然。检查我修改后的答案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-05
        • 2012-09-22
        • 2017-05-28
        • 1970-01-01
        相关资源
        最近更新 更多