交叉类型&(Intersection Types)
交叉类型说简单点就是将多个类型合并成一个类型。大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
class Person {
constructor(public name: string) { }
}
interface Loggable {
log(): void;
}
class ConsoleLogger implements Loggable {
log() {
// ...
}
}
var jim = extend(new Person("Jim"), new ConsoleLogger());
var n = jim.name;
jim.log();
联合类型|(Union Types)
联合类型的语法规则和逻辑 “或” 的符号一致,表示其类型为连接的多个类型中的任意一个。
interface Button {
type: 'default' | 'primary' | 'danger'
text: string
}
const btn: Button = {
type: 'primary',
text: '按钮'
}
类型别名(type)
前面提到的交叉类型与联合类型如果有多个地方需要使用,就需要通过类型别名的方式,给这两种类型声明一个别名。类型别名与声明变量的语法类似,只需要把 const、let 换成 type 关键字即可。
type Alias = T | U
type InnerType = 'default' | 'primary' | 'danger'
interface Button {
type: InnerType
text: string
}
interface Alert {
type: ButtonType
text: string
}
类型索引(keyof)
keyof 类似于 Object.keys ,用于获取一个接口中 Key 的联合类型。
interface Button {
type: string
text: string
}
type ButtonKeys = keyof Button
// 等效于
type ButtonKeys = "type" | "text"
还是拿之前的 Button 类来举例,Button 的 type 类型来自于另一个类 ButtonTypes,按照之前的写法,每次 ButtonTypes 更新都需要修改 Button 类,如果我们使用 keyof 就不会有这个烦恼。
interface ButtonStyle {
color: string
background: string
}
interface ButtonTypes {
default: ButtonStyle
primary: ButtonStyle
danger: ButtonStyle
}
interface Button {
type: 'default' | 'primary' | 'danger'
text: string
}
// 使用 keyof 后,ButtonTypes修改后,type 类型会自动修改
interface Button {
type: keyof ButtonTypes
text: string
}
类型约束(extends)
这里的 extends 关键词不同于在 class 后使用 extends 的继承作用,泛型内使用的主要作用是对泛型加以约束。我们用我们前面写过的 copy 方法再举个例子:
type BaseType = string | number | boolean
// 这里表示 copy 的参数
// 只能是字符串、数字、布尔这几种基础类型
function copy<T extends BaseType>(arg: T): T {
return arg
}
extends 经常与 keyof 一起使用,例如我们有一个方法专门用来获取对象的值,但是这个对象并不确定,我们就可以使用 extends 和 keyof 进行约束。
function getValue<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
const obj = { a: 1 }
const a = getValue(obj, 'a')
类型映射(in)
in关键词的作用主要是做类型的映射,遍历已有接口的key或者是遍历联合类型。下面使用内置的泛型接口Readonly来举例。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Obj {
a: string
b: string
}
type ReadOnlyObj = Readonly<Obj>
首先 keyof Obj 得到一个联合类型 'a' | 'b'。
interface Obj {
a: string
b: string
}
type ObjKeys = 'a' | 'b'
type ReadOnlyObj = {
readonly [P in ObjKeys]: Obj[P];
}
然后 P in ObjKeys 相当于执行了一次 forEach 的逻辑,遍历 'a' | 'b'
type ReadOnlyObj = {
readonly a: Obj['a'];
readonly b: Obj['b'];
}
最后就可以得到一个新的接口。
interface ReadOnlyObj {
readonly a: string;
readonly b: string;
}
条件类型(U ? X : Y)
条件类型的语法规则和三元表达式一致,经常用于一些类型不确定的情况。
T extends U ? X : Y
上面的意思就是,如果 T 是 U 的子集,就是类型 X,否则为类型 Y。下面使用内置的泛型接口 Extract 来举例。
type Extract<T, U> = T extends U ? T : never;
如果 T 中的类型在 U 存在,则返回,否则抛弃。假设我们两个类,有三个公共的属性,可以通过 Extract 提取这三个公共属性。
interface Worker {
name: string
age: number
email: string
salary: number
}
interface Student {
name: string
age: number
email: string
grade: number
}
type CommonKeys = Extract<keyof Worker, keyof Student>
// 'name' | 'age' | 'email'