【问题标题】:Strongly typed rest parameters in TypeScriptTypeScript 中的强类型剩余参数
【发布时间】:2019-08-02 09:45:47
【问题描述】:

如何使用 TypeScript 3.2 定义动态强类型的剩余参数? 这是我的用例:

function exec<T, P extends ICommandNameArgumentTypeMapping, E extends keyof P, U extends P[E]>(command: E, ...rest: U): U{
    return;
}

exec('cmd2', true, 1, 'hello');

interface ICommandNameArgumentTypeMapping {
    ['cmd1']: [string];
    ['cmd2']: [boolean, number, string];
    ['cmd2']: [boolean, boolean];
}

此时似乎一切正常。 当用cmd2exec 编写参数时,我可以看到编译器(打字稿)提供了3 个参数的输入信息。 返回值也是正确的……

但是,在包含声明 ...rest: U 的其余参数的行中,事情发生了变化。

错误很简单: A rest parameter must be of an array type.

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    ...rest时,rest是一个数组:

    const someFunction = (...args) => console.log(args);
    
    someFunction('hello', 'world');

    因此你应该写:

    (command: E, ...rest: U[])
    

    或任何适合您程序所需行为的类似内容。

    【讨论】:

      【解决方案2】:

      问题是P extends ICommandNameArgumentTypeMapping,这意味着exec() 接受作为您定义的接口超集的任何映射。这将允许非数组类型。如果您删除该约束(并修复我认为是错字的地方),您将不会收到任何错误消息。

      interface ICommandNameArgumentTypeMapping {
          ['cmd1']: [string];
          ['cmd2']: [boolean, number, string];
          ['cmd3']: [boolean, boolean];
      }
      
      type P = ICommandNameArgumentTypeMapping;
      
      function exec<T, E extends keyof P, U extends P[E]>(command: E, ...rest: U): U{
          return rest;
      }
      
      exec('cmd2', true, 1, 'hello');
      

      【讨论】:

        【解决方案3】:

        问题

        U 必须是一个数组。我们知道UP 的值,但不能保证P 的所有值都是数组。那是因为exec 不依赖于下面定义的具体的ICommandNameArgumentTypeMapping 接口,而是依赖于一些我们还不完全了解的P。而且因为我们还不知道它,我们不能相信它会遵循ICommandNameArgumentTypeMapping 的蓝图——毕竟,它可能会添加一些自己的属性,而不是数组。

        解决方案

        解决方案是确保所有值(现在和未来)始终是数组。

        interface ICommandNameArgumentTypeMapping {
            ['cmd1']: [string];
            ['cmd2']: [boolean, number, string];
            ['cmd3']: [boolean, boolean];
            [index: string]: any[]
        }
        

        这个额外的属性称为索引签名

        当然,您可以在这里更准确地说 (string | number | boolean)[] 而不是 any[]

        奖励积分

        您的代码中还有一些错误:

        • 计算的属性名称 cmd2 重复
        • T 类型参数未使用
        • P类型参数使用不当(既不用于描述参数也不用于返回类型)
        • exec 承诺返回 U,但它正在返回 undefined

        更正的解决方案:

        function exec<P extends ICommandNameArgumentTypeMapping, E extends keyof P, U extends P[E]>(mapping: P, command: E, ...rest: U): U{
            return rest;
        }
        
        interface ICommandNameArgumentTypeMapping {
            ['cmd1']: [string];
            ['cmd2']: [boolean, number, string];
            ['cmd3']: [boolean, boolean];
            [index: string]: any[]
        }
        
        declare const mapping: ICommandNameArgumentTypeMapping;
        
        exec(mapping, 'cmd2', true, 1, 'hello');
        

        【讨论】:

          猜你喜欢
          • 2014-01-31
          • 1970-01-01
          • 2020-03-03
          • 2019-02-07
          • 2020-06-09
          • 1970-01-01
          • 2016-12-09
          • 1970-01-01
          • 2021-11-15
          相关资源
          最近更新 更多