TypeScript 类型错误何时发生?
TypeScript 是一种静态类型语言。
它通过编译、编辑器扩展等进行类型检查,并在执行前显示类型错误。
什么时候出现类型错误?
为了获得线索,让我们看一下 TypeScript 中的一个真实类型错误。
const x: number = "number"
// ^
// Type 'string' is not assignable to type 'number'.
string类型变为number类型任务你不能。换句话说,当“右侧的值类型”无法分配给“左侧的变量类型”时,似乎发生了错误。
什么情况下可以赋值,什么情况下不能赋值?如果类型相同,当然可以分配。但这还不是全部。
答案是“右侧值的类型”与“左侧变量的类型”相同。部分类型如果可以分配
在这种情况下,类型检查可以通过。在本文中部分类型和相关的变性并在 TypeScript 4.7 中添加可选的方差注释本节说明。
TypeScript 中的部分类型正是结构亚型(结构子类型)。
欲了解更多信息,请参阅这篇文章:结构子类型化_TypeScript简介《生存TypeScript》部分类型
我们经常用集合来描述类型,所以如果我们在这里照样用集合来描述子类型,那么子类型就是子集。
比如
number | string这样的联合类型是一个集合,其值为33和-4这样的数字和"Hello World"和"TypeScript"这样的字符串。此时,number是number | string的子集,对吧?也就是说,
number类型是number | string类型的子类型并且是可赋值的!const x: number = 1 const y: number | string = x // => OK!让我们看另一个例子。接下来是对象类型。
假设我们有以下两种对象类型:Hoge和HogeFuga哪个子类型?type Hoge = { hoge: number } type HogeFuga = { hoge: number, fuga: string }乍一看,
Hoge似乎是HogeFuga的子类型,但事实恰恰相反,HogeFuga是Hoge的子类型。
我认为如果你想象它会如何使用它会更容易理解。当您使用
Hoge类型时,您会遇到一种情况,您希望从其值中提取属性hoge。HogeFuga类型的属性需要hoge和fuga。如果您尝试使用值
HogeFuga,而实际内容为Hoge,则属性fuga不存在并出现错误。相反,即使Hoge的内容是HogeFuga,Hoge也只使用属性hoge,所以它工作正常。事实上,如果你尝试分配每个,前者会导致错误,而后者不会有问题。
const h : Hoge = { hoge: 1 } const hf: HogeFuga = { hoge: 1, fuga: "a" } const x: HogeFuga = h // => Property 'fuga' is missing in type 'Hoge' but required in type 'HogeFuga'. const y: Hoge = hf // => OK同样,结合
Hoge和HogeFuga给出:
Hoge:具有hoge属性的对象的类型(它也可以有其他属性!)HogeFuga:具有hoge和fuga属性的对象类型
Hoge是一个比HogeFuga更广泛的集合,即使综合考虑也是如此,所以HogeFuga是Hoge的子类型。这样,如果你把一个类型看成一个集合,并根据它是否是一个子集来判断一个子类型,那么类型检查在大多数情况下都会通过。
我们将在下一章中介绍一个稍微复杂的案例。类型变量和变异
TypeScript 具有带有类型变量的类型,例如
Array<T>。 (对于Array<T>,T是一个类型变量)
对于此类类型,它们是否是类型变量的子类型变得很重要。比如
Array<number | string>和Array<number>呢?Array<number>不能替换为Array<number | string>。const arrNS: Array<number | string> = [1, "two", 3] const arrN: Array<number> = arrNS // => Error // ^^^^ // Type '(string | number)[]' is not assignable to type 'number[]'. // Type 'string | number' is not assignable to type 'number'. // Type 'string' is not assignable to type 'number'.另一方面,
Array<number>可以分配给Array<number | string>。const arrN: Array<number> = [1,2,3] const arrNS: Array<number | string> = arrN // => OK因此,当
A是B的子类型时,Array<A>成为Array<B>的子类型。
如果某个类型的类型变量是子类型,那么该类型也一定是子类型吗?答案是不。让我们看另一个例子。
作为类型变量,考虑给定函数参数类型的
Func<T>类型。type Func<T> = (x: T) => number让我们用
Func<number>代替Func<number | string>。const funcN: Func<number> = (x: number) => 1 const funcNS: Func<number | string> = funcN // ^^^^^^ // Type 'Func<number>' is not assignable to type 'Func<string | number>'. // Type 'string | number' is not assignable to type 'number'. // Type 'string' is not assignable to type 'number'.我有一个错误。
现在让我们用Func<number | string>代替Func<number>。const funcNS: Func<number | string> = (x: number | string) => 1 const funcN: Func<number> = funcNS // OK已分配!
number是number | string的子类型,但Func<number | string>似乎是Func<number>的子类型。总而言之,它看起来像这样:
- 当
A是B的子类型时,Array<A>是Array<B>的子类型- 当
A是B的子类型时,Func<B>是Func<A>的子类型这样子类型关系根据类型变量的使用方式而改变的属性变性称为(方差)。
有四种类型的更改:
- 协方差(协变)
- 当
A是B的子类型时,Type<A>是Type<B>的子类型- 逆变的(逆变)
- 当
B是A的子类型时,Type<A>是Type<B>的子类型- 双重变化(双变量)
- 当
A是B的子类型或B是A的子类型时,则Type<A>是Type<B>的子类型- 不可变,不可变(不变的,不变的)1
Type<A>是Type<B>的子类型,仅当A和B属于同一类型时
Array<T>可以表示为协变,Func<T>可以表示为逆变。
TypeScript 在许多情况下基本上是协变的,而在函数参数等情况下是逆变的。严格函数类型
我解释了函数参数是逆变的,但实际上,在 TypeScript 中,默认情况下,函数参数是双重变化是。
TypeScript 2.6 中添加的选项strictFunctionTypes使其具有逆变性。strict: true也打开了strictFunctionTypes,所以如果你对阅读本文的类型感兴趣,你应该没问题,但要小心。如果函数参数是双变量的怎么办?
const funcN: Func<number> = (x: number) => Math.abs(x) const funcNS: Func<number | string> = funcN funcNS("apple")因为是双变量,上面的赋值不会出错,所以执行
Math.abs("apple")。
这样如果函数参数不是逆变的,在运行时可能会出错,所以建议开启strictFunctionTypes。可选的方差注释
最后,我们介绍 TypeScript 4.7 中添加的语法 Optional Variance Annotations。
顾名思义,它允许您对变体进行注释。逆变参数写
in,协变参数写out,如下所示。type Func<in T, out S> = (x: T) => S因为它是可选的,所以无论它是否存在,类型都不会改变,但是通过这样描述它,可变性就变得清晰了。
如果您进行了不正确的注释,它也会给出错误。type Func<out T, in S> = (x: T) => S // => Error // ^^^^^ ^^^^ // Type 'Func<sub-T, S>' is not assignable to type 'Func<super-T, S>' as implied by variance annotation. // Types of parameters 'x' and 'x' are incompatible. // Type 'super-T' is not assignable to type 'sub-T'. // Type 'Func<T, super-S>' is not assignable to type 'Func<T, sub-S>' as implied by variance annotation. // Type 'super-S' is not assignable to type 'sub-S'.最后
感谢您阅读到最后!
如果你想听更多的细节,让我们直接在 Dev Talk 中交谈!
参考
- 子类型
- 变性
- 严格函数类型
- 可选的方差注释
我认为 immutable 和 invariable 是指同一个属性的词,但我都写了这两个词,因为网站上的符号不同。参考:https://togetter.com/li/66427 ↩
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308624374.html