本章我们来学习联合类型以及类型别名。
联合类型
在基础类型一文中我们提到过,将变量定义为字符串类型后是不能再赋值为其他类型的,那如果我们需要这个变量既可以是字符串也可以是数值该怎么办?此时我们就可以使用联合类型来实现需求。
联合类型
(Union Types)表示取值可以为多种类型中的一种,除此以外的类型则会报错。举个例子:
let field: string | number
field = 'first' // 成功
console.log(field.length) // 5
field = 1 // 成功
console.log(field.length) // error TS2339: Property 'length' does not exist on type 'number'
field = false // error TS2322: Type 'boolean' is not assignable to type 'string | number'
联合类型使用 |
分隔每个类型,上面例子中 field
既可以是 string
也可以是 number
类型。当它是 string
时可以使用 string
的属性或方法,比如属性 length
;当它是 number
时,因为 number
没有 length
属性,所以会编译报错。
上面的例子是通过 类型推论
得到变量准确的类型,从而可以使用相应类型的属性或方法。当 TypeScript
不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里 共有的
属性或方法。举个例子:
function getLength(something: string | number): number {
return something.length; // error TS2339: Property 'length' does not exist on type 'string | number'. Property 'length' does not exist on type 'number'
}
上面例子中,因为 length
不是 string
和 number
共有的属性,所以会报错。访问 string
和 number
的共有属性是没问题的,举个例子:
function getLength(something: string | number): string {
return something.toString();
}
联合类型不仅可以是基础类型,也可以是接口或者类,举个例子:
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function getSmallPet(): Fish | Bird {
let bird: Bird = {
fly() {},
layEggs() {}
}
return bird
}
let pet = getSmallPet();
pet.layEggs(); // 成功
pet.swim(); // error TS2339: Property 'swim' does not exist on type 'Bird | Fish'.Property 'swim' does not exist on type 'Bird'
pet.fly(); // error TS2339: Property 'fly' does not exist on type 'Bird | Fish'.Property 'fly' does not exist on type 'Fish'
上面例子中联合类型为 Fish
和 Bird
,只有它们共有的方法 layEggs
可以访问,因此 swim
和 fly
都会编译报错。
看了上面的介绍,你可能会觉得 any
也能实现同样的效果,就代码方面而言确实是这样的,将联合类型改为 any
是不会编译报错的,但是与此同时你也失去了 TypeScript
最大的功能。在基础类型一文中有介绍 any
的使用场景,比如在编译阶段不清楚类型的情况下可以使用。
类型别名
类型别名
用来给一个类型起个新名字。起别名的方法很简单,使用type关键字。
type 别名 = 类型(原始类型、联合类型、元组…)
举个例子:
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
起别名不会新建一个类型,它是创建了一个新名字来引用那个类型。比如上面例子,Name 和 string 其实是一样的类型。通常来说给 原始类型
起别名没有特别用处,更多的用于 联合类型
,举个例子:
type Field = string | number
let field: Field
field = 'first'
field = 1
类型别名有时和接口很像,但是可以作用于 原始值
,联合类型
,元组
以及其它
任何你需要手写的类型。类型别名与接口刚接触时可能会混淆,举个例子:
type Field = {
num: number;
str: string;
}
interface inter {
num: number;
str: string;
}
上面例子中类型别名和接口使用上比较类似,但是还是有很明显的区别的,具体区别我会放在下一篇接口篇中细谈。
类型别名是可以使用泛型
的,关于 泛型
的内容会在之后介绍,此处简单讲下在类型别名中的使用。举个例子:
type Container<T> = {
value: T
}
let container: Container<string> = {
value: 'str'
}
此时 value
的类型是根据传递的类型参数决定的。
我们也可以使用类型别名来在属性里引用自己:
type Container<T> = {
value: T;
children?: Container<T>
}
let container: Container<string> = {
value: 'str',
children: {
value: 'str2'
}
}