为什么可以在TypeScript中将接口中可能的数字值转换为类实现中的不可能的数字值?

科恩

今天,我遇到了意外的TypeScript编译器行为。我想知道这是错误还是功能。也许这将是最后一个,但是我想知道其背后的理由。

如果我声明一个参数可以为的接口方法string | number,并创建一个实现该接口的类,则该类方法只能使该参数生效string这会导致这样的情况,即类实现不希望有数字,但是编译器允许传递该数字。为什么允许这样做?

interface Foo {
    hello(value: string | number): void
}

class FooClass implements Foo {
    hello(value: string) { //notice the missing 'number'
        console.log(`hello ${value}`)
    }
}

const x = new FooClass()

x.hello("me")

//x.hello(42) this gives a compile error

const y: Foo = x

y.hello(42)
贾卡尔兹

关于TypeScript的可悲的事实是,它不是完全类型安全的。在感觉到稳健性会阻碍生产率的地方,某些功能是故意不健全的。请参阅《 TypeScript手册》中的“关于健全性的说明”您已经遇到了一个这样的功能:方法参数bivariance

当你有一个接受类型的参数的函数或方法类型A,唯一的类型安全实施或延长它接受的参数的方式 BA这称为参数相反性:如果A扩展B,则((param: B) => void) extends ((param: A) => void)函数的子类型关系与其参数的子类型关系相反因此{ hello(value: string | number): void },用{ hello(value: string | number | boolean): void }or实施它是安全的{ hello(value: unknown): void}

但是您使用来实现它{ hello(value: string): void}该实现正在接受已声明参数子类型这就是协方差函数及其参数的子类型关系相同),并且如您所指出的那样,这是不安全的。打字稿接受两个安全逆变实施和不安全协执行:这就是所谓的bivariance

那么为什么在方法中允许这样做呢?答案是因为许多常用类型具有协变方法参数,而强制使用协变会导致此类无法形成子类型层次结构。FAQ条目中关于参数双方差的激励示例Array<T>将其Array<string>视为的子类型非常方便Array<string | number>毕竟,如果您要求我提供Array<string | number>,而我交给您["a", "b", "c"],那应该可以接受,对吧?好吧,如果您对方法参数严格要求的话,那就不用了。毕竟,一个Array<string | number>应该让您push(123)使用它,而一个Array<string>不应该。因此,方法参数协方差是允许的。


所以,你可以做什么?在TypeScript 2.6之前,所有功能都以这种方式起作用。但是随后他们引入了--strictFunctionTypes编译器标志如果启用了(并且应该启用),则将对函数参数类型进行协变量检查(安全),而对方法参数类型进行双变量检查(不安全)。

类型系统中的函数和方法之间的差异相当细微。类型{ a(x: string): void }{ a: (x: string) => void }相同,除了第一种a是方法,第二种a是函数值属性。因此x,将对第一类型in进行双变量检查,而x对第二类型in将进行反检查。除此之外,它们的行为基本相同。您可以将方法实现为函数值属性,反之亦然。

这导致以下可能的解决方案:

interface Foo {
    hello: (value: string | number) => void 
}

现在hello被声明为函数而不是方法类型。但是类实现仍然可以是一种方法。现在您得到了预期的错误:

class FooClass implements Foo {
    hello(value: string) { // error!
//  ~~~~~
//  string | number is not assignable to string
        console.log(`hello ${value}`)
    }
}

而且,如果您这样离开,则稍后会出现错误:

const y: Foo = x; // error!
//    ~
// FooClass is not a Foo

如果您进行修复FooClass以使其hello()接受的超类型string | number,则这些错误将消失:

class FooClass implements Foo {
    hello(value: string | number | boolean) { // okay now
        console.log(`hello ${value}`)
    }
}

好的,希望能有所帮助;祝好运!

操场上的代码链接

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Java

为什么不可能用不同的类型参数多次实现通用接口?

来自分类Java

Java,不可能将对象强制转换为Float .....为什么?

来自分类Java

为什么在Java中不可能在内部匿名类中引用非最终变量?

来自分类Dev

为什么存储在接口中的值无法在Golang中寻址

来自分类Dev

为什么不可能构建可以确定C ++函数是否会更改特定变量的值的编译器?

来自分类Dev

为什么在C中不可能重载?

来自分类Dev

Lambda表达式中的不可能值

来自分类Dev

为什么在Haskell中不可能分割整数?

来自分类Dev

为什么要在Typescript中创建不可能的交集类型?

来自分类Dev

为什么不可能将const X转换为X&?

来自分类Dev

为什么可以在抽象类中实现接口,而不能在其他接口中实现?

来自分类Dev

SQL如果可能,将值转换为int,如果不可能,则将值设置为0

来自分类Dev

为什么包含不可能的条件分支会更改此方法的返回值?

来自分类Dev

在Python中删除数据中不可能的值

来自分类Dev

为什么不可能将`(()-> Void)`强制转换为`(()-> Void)`?

来自分类Dev

在pandas列中将数字转换为True值

来自分类Dev

为什么在Python中不可能实现真正的不变性?

来自分类Dev

为什么不可能指定Scala varargs参数的默认值?

来自分类Dev

在PHP中将数字转换为字符串值

来自分类Dev

为什么不可能实现“无限”的一个时间片算法?

来自分类Dev

在Excel中将数字字符串转换为值

来自分类Dev

在 int 子类中重写 int 值是不可能的?

来自分类Dev

Numba 插入不可能的值

来自分类Dev

为什么 Newtonsoft 在 F# 中将缺失的整数字段转换为其默认值?

来自分类Dev

为什么 Collection.equals() JavaDoc 说不可能正确实现 List 和 Set?

来自分类Dev

SCSS @if 变量 == 值 @import 不可能?

来自分类Dev

为什么直接使用泛型值是不可能的,但如果从 Dart 中的方法返回也是可能的

来自分类Dev

不可能将字符串对象转换为类对象

来自分类Dev

如何在 Julia 中将数字转换为布尔值?

Related 相关文章

  1. 1

    为什么不可能用不同的类型参数多次实现通用接口?

  2. 2

    Java,不可能将对象强制转换为Float .....为什么?

  3. 3

    为什么在Java中不可能在内部匿名类中引用非最终变量?

  4. 4

    为什么存储在接口中的值无法在Golang中寻址

  5. 5

    为什么不可能构建可以确定C ++函数是否会更改特定变量的值的编译器?

  6. 6

    为什么在C中不可能重载?

  7. 7

    Lambda表达式中的不可能值

  8. 8

    为什么在Haskell中不可能分割整数?

  9. 9

    为什么要在Typescript中创建不可能的交集类型?

  10. 10

    为什么不可能将const X转换为X&?

  11. 11

    为什么可以在抽象类中实现接口,而不能在其他接口中实现?

  12. 12

    SQL如果可能,将值转换为int,如果不可能,则将值设置为0

  13. 13

    为什么包含不可能的条件分支会更改此方法的返回值?

  14. 14

    在Python中删除数据中不可能的值

  15. 15

    为什么不可能将`(()-> Void)`强制转换为`(()-> Void)`?

  16. 16

    在pandas列中将数字转换为True值

  17. 17

    为什么在Python中不可能实现真正的不变性?

  18. 18

    为什么不可能指定Scala varargs参数的默认值?

  19. 19

    在PHP中将数字转换为字符串值

  20. 20

    为什么不可能实现“无限”的一个时间片算法?

  21. 21

    在Excel中将数字字符串转换为值

  22. 22

    在 int 子类中重写 int 值是不可能的?

  23. 23

    Numba 插入不可能的值

  24. 24

    为什么 Newtonsoft 在 F# 中将缺失的整数字段转换为其默认值?

  25. 25

    为什么 Collection.equals() JavaDoc 说不可能正确实现 List 和 Set?

  26. 26

    SCSS @if 变量 == 值 @import 不可能?

  27. 27

    为什么直接使用泛型值是不可能的,但如果从 Dart 中的方法返回也是可能的

  28. 28

    不可能将字符串对象转换为类对象

  29. 29

    如何在 Julia 中将数字转换为布尔值?

热门标签

归档