交叉类型(Intersection Types)
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子:
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)
联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 number或 string类型的参数。 例如下面的函数:
// any类型function padLeft(value: string, padding: any) { }// 这里采用联合类型,对类型限制为string和numberfunction padLeft(value: string, padding: string | number) {}
类型保护与区分类型(Type Guards and Differentiating Types)
对于联合类型,也可包括自定义类型, 像这种情况调用 Bird.swim() 就会报错, 如何区分呢?
// 联合类型也可以包括自定义类型interface Bird {fly();layEggs();}interface Fish {swim();layEggs();}function getSmallPet(): Fish | Bird {// ...}let pet = getSmallPet();pet.layEggs(); // okaypet.swim(); // errors
用户自定义的类型保护
用自定义方法判断类型差别,自行编码实现.
function isFish(pet: Fish | Bird): pet is Fish {return (<Fish>pet).swim !== undefined;}if (isFish(pet)) {pet.swim();}else {pet.fly();}
typeof类型保护
function padLeft(value: string, padding: string | number) {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}'.`);}
instanceof类型保护
// 联合类型也可以包括自定义类型interface Bird {fly();layEggs();}interface Fish {swim();layEggs();}function getSmallPet(obj): Fish | Bird {// ...return obj;}class Dock implements Bird {fly(){console.log('dock fly...');}layEggs(){console.log('dock layeggs...');}}var dark = new Dock();let pet = getSmallPet(dark);pet.layEggs(); // okayif(dark instanceof Dock){dark.fly(); // errors}
null和undefined
TypeScript具有两种特殊的类型, null和 undefined,它们分别具有值null和undefined.
注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。
可选参数和可选属性
使用了 --strictNullChecks,可选参数会被自动地加上 | undefined:
function f(x: number, y?: number) {return x + (y || 0);}f(1, 2);f(1);f(1, undefined); // 默认认为可选参数为undefinded; 在ts中这和null是有区别的f(1, null); // error, 'null' is not assignable to 'number | undefined'// class Cclass C {a: number;b?: number;}let c = new C();c.a = 12;c.a = undefined; // error, 'undefined' is not assignable to 'number'c.b = 13;c.b = undefined; // okc.b = null; // error, 'null' is not assignable to 'number | undefined'
语法!
如果编译器不能够去除 null或 undefined,你可以使用类型断言手动去除。 语法是添加 !后缀:
interface UserI{name: string;}function foo(obj?: UserI){// 这里如果没有! 会报错误提示 obj可能为undefinedconst name = obj!.name;console.log(name);}foo();
type和interface
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();}}
像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。
共同点:
type和interface都可以描述一个对象或者函数.
interface User {name: string;age: number;}interface setAge{(age: number): number;}type User1 = {name: string;age: number;}type setAge1 = (age: number) => number;
区别1:
拓展(extends)与 交叉类型(Intersection Types) .
interface 可以 extends, 但 type 是不允许 extends 和 implement 的,但是 type 缺可以通过交叉类型 实现 interface 的 extend 行为,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 与 interface 类型 交叉 。
interface extends interface
interface Name {name: string;}interface User extends Name {age: number;}const zs: User = {name: '张三',age: 30}
interface extends type
type Name = {name: string;}interface User extends Name {age: number;}const zs: User = {name: '张三',age: 30}console.log(zs);;
type 与 type 联合
type Name = {name: string;}type User = Name & { age: number };const zs: User = {name: '张三',age: 30}console.log(zs);;
type 与 interface 交叉
interface Name {name: string;}type User = Name & {age: number;}const zs: User = {name: '张三',age: 30}console.log(zs);;
区别2:
type 可以声明基本类型别名,联合类型,元组等类型,但interface不行.
// 基本类型别名type Name = string; // 虽然没有实质性作用// 联合类型type Foo = number | string;// typeof赋值interface Person {name: string;age: number;}const sem: Person = { name: "semlinker", age: 30}type Sem = typeof sem; // type Sem = Person
总结:
最重要区别是类型别名不能被 extends和 implements(自己也不能 extends和 implements其它类型)。 因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。当然如果只是定义一个数据类型的话,type和interface是 一样的.
其他
其他一些高级复杂的用法,实际开发碰到再查阅相关资料.
这里列列举的只是在常规开发中,很大可能性会碰到的,必须先知道的知识点.
