添加类型信息
类型转换
类型转换将一个表达式的类型转换为另一个类型。每个编程语言都制定了自己的规则,决定哪些转换是合法的,哪些是不合法的,哪些能够由编译器自动完成,哪些必须使用额外的代码来完成
显式类型转换允许我们告诉编译器将某个值视为特定的类型处理。在 TypeScript 中,通过在值的前面添加<NewType>或者在值的后面添加as NewType来将其转换为NewType
隐式和显式类型转换
隐式类型转换是编译器自动执行的一种类型转换,并不需要编写任何代码。这种转换通常是安全的。与之相对地,显式类型转换指的是需要我们编写代码进行指定的类型转换。这种类型转换实际上会绕过类型系统的规则,所以应该谨慎使用。
向上转换和向下转换
子类型的对象解释为父类型是一种常见的类型转换。如果基类是Shape,从其派生了一个Triangle,那么在任何需要Shape的地方,总是可以使用一个Triangle
子类的对象解释为父类型称为向上转换,如示例:
1
2
3
4
5
6
7
8
9
10
11
12
13class Shape {}
declare const triangleType: unique symbol;
class Triangle extends Shape {
[triangleType]: void;
}
function useShape(shape: Shape) {}
let myTriangle: Triangle = new Triangle();
useShape(myTriangle);在
useShape()中,即使传入一个Triangle作为实参,编译器也会把它当作个Shape。将派生类(Triangle)解释为基类(Shape)的做法称为向上转换。如果准确知道我们的 Shape 实际上是一个Triangle,就可以把它转换回Triangle,但是这种转换是显式转换。从父类转换到派生类称为向下转,示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Shape {}
declare const triangleType: unique symbol;
class Triangle extends Shape {
[triangleType]: void;
}
function useShape(shape: Shape, isTriangle: boolean) {
if (isTriangle) {
let triangle: Triangle = <Triangle>shape;
return triangle;
}
}
let myTriangle: Triangle = new Triangle();
useShape(myTriangle, true);与向上转换不同,向下转换不是安全的。虽然从派生类很容易知道它的父类,但是在给定父类时,编译器无法自动确定某个值是多个派生类中的哪一个。
拓宽转换和缩窄转换
另外一种常见的隐式转换是从固定位数的整数类型(例如一个 8 位无符号整数)转换为另外一个更多位数的整数类型(例如一个 16 位无符号整数)。之所以可以隐式地完成这种转换,是因为 16 位无符号整数可以表示所有 8 位无符号整数值。这种类型的转换称为拓宽转换。
另一方面,将一个带符号整数转换为无符号整数是危险的操作,因为无符号整数无法表示负数。类似地,将位数更多的整数转换为位数更少的整数,例如将 16 位无符号整数转换为 8 位无符号整数,只能用于小类型可以表示的值。这种类型的转换称为缩窄转换。因为缩窄转换具有危险性,所以编译器要求必须显式指定这种转换。
类型转换会绕过类型检查器,实际上是避开了类型检查带给我们的好处,所以不应该轻易使用转换。不过,它们是有用的工具,当我们知道的信息比编译器更多,并想让编译器接受这些信息的时候尤为如此。当我们告诉编译器这些信息后,它就可以利用这些信息做进一步分析。
隐藏和恢复类型信息
异构集合
基本类型或接口,例如:
1
2
3
4
5
6
7
8
9
10interface IDocumentItem {}
class Pph implements IDocumentItem {}
class Pic implements IDocumentItem {}
class Tab implements IDocumentItem {}
class MyDocument {
items: IDocumentItem[];
}和类型或变体
和类型,例如
1
2
3
4
5
6
7
8
9
10class Pph {}
class Pic {}
class Tab {}
// 两种方式都可以
// class MyDocument {
// items: (Pph | Pic | Tab)[];
// }
class MyDocument<Pph, Pic, Tab> {
items;
}1
unknown 类型:表示文档中可以包含任何东西。类型之间不需要有共享的契约,我们甚至不需要提前知道这些类型能做什么。此外,能够对这个集合的元素做的操作更少。要处理它们,几乎总是需要把它们转换为其他类型,所以必须在另外一个数组中跟踪它们的原始类型。
序列化
我们仍然知道值的类型,但是类型检查器不再知道。其反向操作是,接受一个序列化后的对象,将其转换回带类型的值。在这里,我们可以使用JSON.parse()方法,它接受一个字符串,返回一个 JavaScript 对象。因为这种技术适用于任何字符串,所以调用该方法的结果是 any 类型。
any 类型:
TypeScript 提供了 any 类型。该类型用于当类型信息不可用时,与 JavaScript 进行互操作。any 是一个危险的类型,因为它可以自由转换为其他类型,其他类型也可以自由转换为 any 类型,编译器不对该类型的实例进行类型检查。开发人员有责任确保不会发生错误解释类型的情况。unknown 类型比 any 类型更安全