【问题标题】:How can I get type safety for returned functions in TypeScript?如何获得 TypeScript 中返回函数的类型安全性?
【发布时间】:2016-10-18 01:19:20
【问题描述】:

这个 TypeScript 编译得很好:

abstract class Animal {
    /*
    Any extension of Animal MUST have a function which returns
    another function that has exactly the signature (string): void
     */
    abstract getPlayBehavior(): (toy: string) => void;
}

class Cat extends Animal {
    /*
    Clearly does not have a function which returns a function
    that has the correct signature. This function returns a function with
    the signature (void) : void
    */
    getPlayBehavior() {
        return () => {
            console.log(`Play with toy_var_would_go_here!`);
        };
    }
}

class Program {
    static main() {
        let cat: Animal = new Cat();
        cat.getPlayBehavior()("Toy");
    }
}

Program.main();

我期待一个错误,因为 Cat 类肯定没有实现抽象 Animal 类正确。我希望 Cat 类必须有一个函数,该函数返回抽象 Animal 类中指定的确切签名的另一个函数。

运行代码,我得到:

> node index.js
> Play with toy_var_would_go_here!

我可以做些什么来确保编译器执行这种策略?

【问题讨论】:

  • 在 JavaScript 中,一元函数一元函数。传递的额外参数被简单地忽略。这在事件处理中很常见。所以你的函数是正确的签名。
  • @FengyangWang:我不反对,只是要说 TypeScript 做了很多事情,而这些事情并不是仅仅作为 JavaScript 发出的,因此可以强制执行类型安全。我只是好奇我是否能找到一种方法来强制执行这种类型安全。

标签: typescript type-safety


【解决方案1】:

您不会收到错误消息,因为在 javascript/typescript 中,如果您不想使用参数,则不必强制声明参数,只要不存在矛盾即可。

例如Array.forEach的签名是:

forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;

但这会编译得很好:

let a = [1, 2, 3];
a.forEach(item => console.log(item));

这是一件好事,如果我必须拥有所有论据,即使我不使用它们,那将是可怕的。
这里也一样:

type MyFn = (s: string) => void;
let fn: MyFn = () => console.log("hey");

如果我不需要使用字符串参数,那么我可以忽略它,或者我什至可以这样做:

let fn: MyFn = () => console.log(arguments);

如果您将在Cat.getPlayBehavior 中返回的函数的签名更改为与Animal 中的定义相矛盾的内容,那么您将收到错误消息:

class Cat extends Animal {
    getPlayBehavior() {
        return (n: number) => {
            console.log(`Play with toy_var_would_go_here!`);
        };
    }
}

错误:

Class 'Cat' incorrectly extends base class 'Animal'.
  Types of property 'getPlayBehavior' are incompatible.
    Type '() => (n: number) => void' is not assignable to type '() => (toy: string) => void'.
      Type '(n: number) => void' is not assignable to type '(toy: string) => void'.
        Types of parameters 'n' and 'toy' are incompatible.
          Type 'string' is not assignable to type 'number'.

【讨论】:

  • 我个人认为严格要求函数根据其基类/接口显式定义并不可怕。例如,如果函数具有可选参数,则可以将其显式定义为可选参数。如果这不是一个好的默认值,那么像严格的 {} 块这样的东西对于那些更习惯于明确和严格定义的类型的开发人员来说会很有用。
  • 嗯,这就是 javascript 的工作方式,并且 typescript 也允许这样做。如果传递的函数不需要使用参数,那么它只是不将它们包含在签名中。正如我所说,只要没有矛盾,这就是有效的。可选参数意味着别的东西,它意味着它可能会或可能不会被传递,但在这里你说你不在乎那个参数是否被传递,你只是不需要它。
  • 嗯,好的。听起来我在 TypeScript 中寻找的东西目前不存在,我的想法最好作为他们网站上的建议。感谢您的帮助!
  • 欢迎您将其传达给打字员,但我很确定这是设计使然,而且是有道理的。类型安全在这里运作良好,因为您不能定义接收除一个字符串之外的其他内容的签名。如果此函数需要此字符串,它将添加参数,但如果不需要,则该函数仍然满足类型要求。
【解决方案2】:

我期待一个错误,因为 Cat 类肯定没有正确实现抽象 Animal 类

因为类型兼容性。一个不带任何参数的函数(比如foo)可以分配给一个带参数的函数(比如bar)。

原因:bar 没有使用,foo 运行所需的所有信息都将不存在。

更多

这里也有介绍:https://basarat.gitbooks.io/typescript/content/docs/types/type-compatibility.html#number-of-arguments

【讨论】:

  • 一般来说,抽象和接口是契约协议。实现明确地遵循这些接口/抽象的定义方式。如果许多方法开始出现不需要接口中函数的变量,则应该考虑设计。我正在寻找一种方法来强制执行这种严格的实施。
猜你喜欢
  • 2021-06-16
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
  • 2020-07-10
  • 2019-07-17
  • 2020-08-31
  • 2019-02-03
  • 1970-01-01
相关资源
最近更新 更多