交叉类型(Intersection Types)

交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。 例如, Person & Serializable & Loggable同时是 Person Serializable Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。
我们大多是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种情况的场合很多!) 下面是如何创建混入的一个简单例子:

  1. function extend<T, U>(first: T, second: U): T & U {
  2. let result = <T & U>{};
  3. for (let id in first) {
  4. (<any>result)[id] = (<any>first)[id];
  5. }
  6. for (let id in second) {
  7. if (!result.hasOwnProperty(id)) {
  8. (<any>result)[id] = (<any>second)[id];
  9. }
  10. }
  11. return result;
  12. }
  13. class Person {
  14. constructor(public name: string) { }
  15. }
  16. interface Loggable {
  17. log(): void;
  18. }
  19. class ConsoleLogger implements Loggable {
  20. log() {
  21. // ...
  22. }
  23. }
  24. var jim = extend(new Person("Jim"), new ConsoleLogger());
  25. var n = jim.name;
  26. jim.log();

联合类型(Union Types)

联合类型与交叉类型很有关联,但是使用上却完全不同。 偶尔你会遇到这种情况,一个代码库希望传入 numberstring类型的参数。 例如下面的函数:

  1. // any类型
  2. function padLeft(value: string, padding: any) { }
  3. // 这里采用联合类型,对类型限制为string和number
  4. function padLeft(value: string, padding: string | number) {}

类型保护与区分类型(Type Guards and Differentiating Types)

对于联合类型,也可包括自定义类型, 像这种情况调用 Bird.swim() 就会报错, 如何区分呢?

  1. // 联合类型也可以包括自定义类型
  2. interface Bird {
  3. fly();
  4. layEggs();
  5. }
  6. interface Fish {
  7. swim();
  8. layEggs();
  9. }
  10. function getSmallPet(): Fish | Bird {
  11. // ...
  12. }
  13. let pet = getSmallPet();
  14. pet.layEggs(); // okay
  15. pet.swim(); // errors

用户自定义的类型保护
用自定义方法判断类型差别,自行编码实现.

  1. function isFish(pet: Fish | Bird): pet is Fish {
  2. return (<Fish>pet).swim !== undefined;
  3. }
  4. if (isFish(pet)) {
  5. pet.swim();
  6. }
  7. else {
  8. pet.fly();
  9. }

typeof类型保护

  1. function padLeft(value: string, padding: string | number) {
  2. if (typeof padding === "number") {
  3. return Array(padding + 1).join(" ") + value;
  4. }
  5. if (typeof padding === "string") {
  6. return padding + value;
  7. }
  8. throw new Error(`Expected string or number, got '${padding}'.`);
  9. }

instanceof类型保护

  1. // 联合类型也可以包括自定义类型
  2. interface Bird {
  3. fly();
  4. layEggs();
  5. }
  6. interface Fish {
  7. swim();
  8. layEggs();
  9. }
  10. function getSmallPet(obj): Fish | Bird {
  11. // ...
  12. return obj;
  13. }
  14. class Dock implements Bird {
  15. fly(){
  16. console.log('dock fly...');
  17. }
  18. layEggs(){
  19. console.log('dock layeggs...');
  20. }
  21. }
  22. var dark = new Dock();
  23. let pet = getSmallPet(dark);
  24. pet.layEggs(); // okay
  25. if(dark instanceof Dock){
  26. dark.fly(); // errors
  27. }

null和undefined

TypeScript具有两种特殊的类型, nullundefined,它们分别具有值null和undefined.
注意,按照JavaScript的语义,TypeScript会把 nullundefined区别对待。 string | nullstring | undefinedstring | undefined | null是不同的类型。

可选参数和可选属性

使用了 --strictNullChecks,可选参数会被自动地加上 | undefined:

  1. function f(x: number, y?: number) {
  2. return x + (y || 0);
  3. }
  4. f(1, 2);
  5. f(1);
  6. f(1, undefined); // 默认认为可选参数为undefinded; 在ts中这和null是有区别的
  7. f(1, null); // error, 'null' is not assignable to 'number | undefined'
  8. // class C
  9. class C {
  10. a: number;
  11. b?: number;
  12. }
  13. let c = new C();
  14. c.a = 12;
  15. c.a = undefined; // error, 'undefined' is not assignable to 'number'
  16. c.b = 13;
  17. c.b = undefined; // ok
  18. c.b = null; // error, 'null' is not assignable to 'number | undefined'

语法!

如果编译器不能够去除 nullundefined,你可以使用类型断言手动去除。 语法是添加 !后缀:

  1. interface UserI{
  2. name: string;
  3. }
  4. function foo(obj?: UserI){
  5. // 这里如果没有! 会报错误提示 obj可能为undefined
  6. const name = obj!.name;
  7. console.log(name);
  8. }
  9. foo();

type和interface

type称为类型别名, 是给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。

  1. // 起别名不会新建一个类型 - 它创建了一个新 名字来引用那个类型。 给原始类型起别名通常没什么用
  2. type Name = string; // 没有意义
  3. type NameResolver = () => string;
  4. type NameOrResolver = Name | NameResolver;
  5. function getName(n: NameOrResolver): Name {
  6. if (typeof n === 'string') {
  7. return n;
  8. }
  9. else {
  10. return n();
  11. }
  12. }

像我们提到的,类型别名可以像接口一样;然而,仍有一些细微差别。

共同点:

type和interface都可以描述一个对象或者函数.

  1. interface User {
  2. name: string;
  3. age: number;
  4. }
  5. interface setAge{
  6. (age: number): number;
  7. }
  8. type User1 = {
  9. name: string;
  10. age: number;
  11. }
  12. 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

  1. interface Name {
  2. name: string;
  3. }
  4. interface User extends Name {
  5. age: number;
  6. }
  7. const zs: User = {
  8. name: '张三',
  9. age: 30
  10. }

interface extends type

  1. type Name = {
  2. name: string;
  3. }
  4. interface User extends Name {
  5. age: number;
  6. }
  7. const zs: User = {
  8. name: '张三',
  9. age: 30
  10. }
  11. console.log(zs);;

type 与 type 联合

  1. type Name = {
  2. name: string;
  3. }
  4. type User = Name & { age: number };
  5. const zs: User = {
  6. name: '张三',
  7. age: 30
  8. }
  9. console.log(zs);;

type 与 interface 交叉

  1. interface Name {
  2. name: string;
  3. }
  4. type User = Name & {
  5. age: number;
  6. }
  7. const zs: User = {
  8. name: '张三',
  9. age: 30
  10. }
  11. console.log(zs);;

区别2:

type 可以声明基本类型别名,联合类型,元组等类型,但interface不行.

  1. // 基本类型别名
  2. type Name = string; // 虽然没有实质性作用
  3. // 联合类型
  4. type Foo = number | string;
  5. // typeof赋值
  6. interface Person {
  7. name: string;
  8. age: number;
  9. }
  10. const sem: Person = { name: "semlinker", age: 30}
  11. type Sem = typeof sem; // type Sem = Person

总结:

最重要区别是类型别名不能被 extendsimplements(自己也不能 extendsimplements其它类型)。 因为 软件中的对象应该对于扩展是开放的,但是对于修改是封闭的,你应该尽量去使用接口代替类型别名。当然如果只是定义一个数据类型的话,type和interface是 一样的.

其他

其他一些高级复杂的用法,实际开发碰到再查阅相关资料.
这里列列举的只是在常规开发中,很大可能性会碰到的,必须先知道的知识点.