⒈交叉类型(Intersection Types)
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable
同时是 Person
和 Serializable
和 Loggable
。 就是说这个类型的对象同时拥有了这三种类型的成员。
每当我们正确的使用交叉类型的时候,TypeScript可以帮我们合理地将两个不同类型叠加为新的类型,并包含了所需的所有类型。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!)
type newType = number & string; let a : newType; interface A{ a:number, b:string, } interface B{ c:string, d:string, } type newType2 = A & B; let b : newType2;
这里的Type关键字是用来声明类型变量的。在运行时,与类型相关的代码都会被移除掉,并不会影响到JavaScript的执行。
*当交叉类型中有属性冲突时,则无论如何赋值都不可能通过类型检查。如下面的代码所示:
interface A{ a:number, b:string, } interface B{ c:string, d:string, } type newType = A & B; let a : newType = {a:1,b:'',c:'',d:''}; a.a = 1; a.b = ''; a.c = ''; a.d = 5; //Error,无法通过类型检查
⒉联合类型(Union Types)
联合类型与交叉类型类似,但使用上却完全不同。
例如我们需要一个变量可能是number,也有可能是string,这是一个很常见的场景,联合类型便是用于解决这样的问题。
比如下面这段经典的函数:
function padLeft(value: string, padding: any) { if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; } if (typeof padding === "string") { return padding + value; } throw new Error(`Expected string or number, got '${padding}'.`); } padLeft("Hello world", 4); // returns " Hello world"
padLeft函数
存在一个问题, padding
参数的类型指定为 any
。 也就是说,我们可以传入一个既不是 number
也不是 string
类型的参数,但是TypeScript却不报错。
let indentedString = padLeft("Hello world", true); // 编译阶段通过,运行时报错
在传统的面向对象语言里,我们可以使用重载或将这两种类型抽象成有层级的类型(父类与子类)。 这么做显然是非常清晰的,但同时也存在了过度设计。
因为在JavaScript中并没有重载可以使用(可以使用特殊的方式创建出类似重载的函数),因此在JavaScript的函数中手动去判断参数的类型这种操作更为常见,这在一定程度上避免了过度设计。
padLeft
原始版本的好处之一是允许我们传入原始类型。 这样做的话使用起来既简单又方便。 如果我们就是想使用已经存在的函数的话,这种新的方式就不适用了。
如果我们希望更准确的描述padding的类型,就可以使用联合类型将padding的类型限定为既可以是number又可以是string。
代替 any
, 我们可以使用 联合类型作为 padding
的参数:
function padLeft(value: string, padding: string | number) { // ... } let indentedString = padLeft("Hello world", true); // 编译器报错,类型true的参数不能赋值给类型string|number的参数
联合类型表示一个变量可以是几种类型之一。 我们用竖线( |
)分隔每个类型,所以 number | string | boolean
表示一个值可以是 number
、string
或 boolean
。
注意,如果一个值是联合类型,我们只能访问它们共有的属性。
我们来看一下下面的例子: