共计 2167 个字符,预计需要花费 6 分钟才能阅读完成。
简介
此为高级类型的第一部分,学习交叉类型 (Intersection Types)、联合类型(Union Types) 以及在使用以上类型时如何区分具体类型。
交叉类型(Intersection Types)
交叉类型把几个类型的成员合并,形成一个拥有这几个类型所有成员的新类型。从字面上理解,可能会误认为是把取出几个类型交叉的(即交集)成员。
交叉类型的使用场景:Mixins、不适合用类来定义。
我感觉,交叉类型和 Mixins 有一点区别:交叉类型只是一个类型声明,用于类型约束;Mixins 会给类增加成员,new 对象时,对象会包含增加的成员属性。
我们看一看示例:
改自官方示例,官方示例有 2 个小问题:会提示不存在属性“hasOwnProperty”;另外,es6 下 prototype 是不可以枚举的,无法通过枚举合并类属性。
interface IAnyObject {[prop: string]: any
}
function extend<First extends IAnyObject, Second extends IAnyObject>(first: First, second: Second): First & Second {const result: Partial<First & Second> = {};
for (const prop in first) {if (first.hasOwnProperty(prop)) {(result as First)[prop] = first[prop];
}
}
for (const prop in second) {if (second.hasOwnProperty(prop)) {(result as Second)[prop] = second[prop];
}
}
return result as First & Second;
}
interface IPerson {
name: string,
age: number
}
interface IOrdered {
serialNo: number,
getSerialNo(): number}
const personA: IPerson = {
name: 'Jim',
age: 20
}
const orderOne: IOrdered = {
serialNo: 1,
getSerialNo: function () { return this.serialNo}
}
const personAOrdered = extend<IPerson, IOrdered>(personA, orderOne);
console.log(personAOrdered.getSerialNo());
- 其中 First & Second 就是交叉类型的写法。
- 通过扩展,可以合并两传入对象的成员属性,增强现有对象的能力。
联合类型(Union Types)
联合类型与交叉类型类似,都可以拥有多个类型的能力,区别是:联合类型一次只能一种类型;而交叉类型每次都是多个类型的合并类型。
联合类型在实际项目中,使用场景比交叉类型广泛得多。
- 字符串填充:
function padLeft(value: string, padding: number | string) {if (typeof padding === "number") {return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {return padding + value;}
return value;
}
padLeft("Hello world", 4);
- 再举个常见的例子,常量的联合类型,五分制考试打分:
type Scores = 1 | 2 | 3 | 4 | 5;
function rating(score: Scores) {console.log(`Set score ${score}`);
}
rating(3);
// error
// rating(6);
枚举也可以实现,但是这样更简洁。
- 还可以用于可为 null 的参数:
function f(sn: string | null): string {if (typeof sn === null) {return 'default';} else {return sn;}
}
- 还可以使用类型别名定义联合类型(上面场景 2 中用过):
type Method = 'get' | 'post';
类型保护和类型区分
使用联合类型时,我们是无法知道编译时的具体类型的,所以在运行时必须要确定类型。所以代码中需要做类型保护和类型区分。
为什么叫类型保护?因为在运行时,必须知道明确类型,否则可能会访问一个不存在的成员属性,导致程序出错。
-
自定义类型保护
// 先判断是否存在成员属性 if(pet.swim) {}
-
in 操作符
// 先判断是否存在成员属性 if('swim' in pet) {}
-
typeof 操作符
function f(sn: string | number): string {if (typeof sn === 'number') {return sn.toString(); } else {return sn;} }
-
instanceof 操作符
// 判断实例原型 if(pet instanceof Fish) {}
-
类型断言
function f(sn: string | null): string {if (sn === null) {return "default";} else {return sn;} }
联合类型应用极广泛,可以多实践。
正文完
发表至: javascript
2019-10-11