openlang

Typescript 可调用函数的约定

为什么 Typescript 接受我的函数定义,虽然它与接口签名不同,但当我实际调用同一个函数时却报告问题。

  interface ITextRenderer {
      (text: string, capitalize : boolean): string;
  };

  //No compiler diagnostic at this assignment.
  var capitalRenderer: ITextRenderer = function (text: string) {
      return text.capitalizeCase();
  }  

  capitalRenderer("demo"); //A diagnostic occurs at this call instead.
从英语翻译查看原文

后续讨论 2

顺着彼此的依据读下去
  1. 固定链接

    接口要求实现该接口的函数的每个调用者都提供两个必需的参数,也就是 text 和 capitalize。

    TypeScript 知道 JavaScript 允许传入额外但不使用的参数,因此允许实现省略这些参数。

    为什么这样可以?因为这让你能替换不同的实现,而不必修改调用代码。

    比如,下面两种 ITextRenderer 实现都能替换进去,而且代码仍然可以运行。

      interface ITextRenderer {
          (text: string, capitalize: boolean): string;
      };
    
      var capitalRenderer: ITextRenderer = function (text: string) {
          return text.capitalizeCase();
      }
    
      var flexibleRenderer: ITextRenderer = function (text: string, capitalize: boolean) {
          if (capitalize) {
              return text.capitalizeCase();
          }
    
          return text.toLowerCase();
      }
    
      // Either implementation can go here
      //var renderer = capitalRenderer;
      var renderer = flexibleRenderer;
    
      renderer("demo", true);

    如果 TypeScript 不允许这样做,capitalRenderer 就必须声明一个正文完全不使用的 capitalize 参数,这会让函数更难读。

    从英语翻译查看原文
  2. 固定链接

    被采纳的解释有误导性,因为它可能让人为了绕过类型检查,在生产代码里试着添加额外参数,而生产环境本来就不会执行这种检查。

    要让类型检查接受参数数量不同的形式,应当明确把这些形式声明为重载,例如下面这样

      interface ITextRenderer {
          (text: string, capitalize: boolean): string;
          (text: string): string;
      };
    
      var capitalRenderer: ITextRenderer = function (text: string) {
          return text.capitalizeCase();
      }  
    
      capitalRenderer("demo"); // selects the single-parameter signature
      capitalRenderer("demo","false"); // selects the double-parameter signature

    但这仍然没有回答为什么 TypeScript 对函数定义和函数调用采用不同的检查。在没有官方文档解释的情况下,可以猜测这是 TypeScript 用 TypeScript / JavaScript 实现带来的技术限制,并看看用 Go 实现的 TypeScript 7 是否会改变这种情况。https://github.com/microsoft/typescript-go?tab=readme-ov-file#typescript-7

    从英语翻译查看原文

同一个问题的其他角度

  1. 初始化以字符串数组为值的 TypeScript Map

    我在创建一个 Map,其中每个条目都保存字符串数组。怎样在一条语句中同时声明类型并完成初始化? 我(天真地)这样试了一下。 结果是下面这个错误。

    回复 2最近活动 typescript
  2. Solidity 从 address 转换为 uint256 时失败

    我创建了一个包含结构体的数组,然后尝试从该数组取得每个账号的值。但传入包含 msg.sender 的 address 变量时失败了,因为它的类型似乎无法转换为 uint256。应该怎样实现?

    回复 3最近活动 solidity
  3. 只读的 List<Int> 算快照吗?

    List<Int> 是只读视图,不是快照。它只告诉使用方,通过这个引用可以调用哪些操作,并不保证它背后的集合不会变。 比如 val owned = mutableListOf(2, 4)。调用方把 owned 传给一个接收 List<Int> 的函数…

    回复 4最近活动 kotlin