一、交叉类型与联合类型

表示一个对象类型的并集与交集
1,交叉类型是将多个类型合并为一个类型,定义的对象必须包含所有的类型,使用“&”表示
2,联合类型:如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员,使用“|”表示
举例:

  1. interface ia {
  2. name: { attr1: string };
  3. }
  4. interface ib {
  5. name: { attr2: number };
  6. age: number;
  7. }
  8. // 交叉类型所有属性都要有
  9. const jiaocha: ia & ib = { name: { attr1: "11", attr2: 22 }, age: 16 };
  10. // 联合类型有其中一种属性就好了
  11. const lianhe: ia | ib = { name: { attr1: "1" } };

二、类型保护与区分类型

如果想要访问这种json对象的方法swim不报错,有两种方法:

  1. interface Bird {
  2. fly: () => void;
  3. layEggs: () => void;
  4. }
  5. interface Fish {
  6. swim: () => void;
  7. layEggs: () => void;
  8. }
  9. const getSmallPet = (): Fish | Bird => {
  10. const swim = () => {
  11. console.log("do swim");
  12. };
  13. const layEggs = () => {
  14. console.log("do layEggs");
  15. };
  16. return { swim, layEggs };
  17. };
  18. // 为什么需要断言保护:因为访问报错
  19. const pet = getSmallPet();
  20. pet.layEggs(); // 正常访问:因为是交集所以可以访问
  21. // pet.swim(); // 访问报错:交集之外,就访问不了,就需要用到类型保护区分类型

1,断言

  1. // 优点,方便使用;缺点,冗余,如果少次访问的时候可以使用,且容易出错
  2. (pet as Fish).swim();

2,用户自定义的类型保护

  1. const isFish = (pet: Fish | Bird): pet is Fish => {
  2. // 使用类型谓词,多次访问时使用
  3. // pet is Fish就是类型谓词。 parameterName必须是来自于当前函数签名里的一个参数名。
  4. return (<Fish>pet).swim !== undefined;
  5. };
  6. // 'swim' 和 'fly' 调用都没有问题了
  7. if (isFish(pet)) {
  8. pet.swim();
  9. } else {
  10. // 注意:TypeScript不仅知道在if分支里pet是Fish类型;它还清楚在else分支里,一定不是Fish类型,而是Bird类型。
  11. pet.fly();
  12. }

3,typeof类型保护

  1. const padLeft = (value: string, padding: string | number) => {
  2. // 缺点,只能访问简单类型,比如string、number、boolen;优点,方便
  3. if (typeof padding === "number") {
  4. console.log(Number(padding) + value);
  5. }
  6. if (typeof padding === "string") {
  7. console.log(padding + value);
  8. }
  9. };
  10. padLeft(" fish", 1);
  11. padLeft(" fish", "one");

4,instanceof类型保护

  1. interface Animal {
  2. name(): string;
  3. }
  4. class MyFish implements Animal {
  5. constructor(private numSpaces: number) {}
  6. name() {
  7. return String(this.numSpaces);
  8. }
  9. }
  10. class MyBird implements Animal {
  11. constructor(private value: string) {}
  12. name() {
  13. return this.value;
  14. }
  15. }
  16. function getRandomAnimal() {
  17. return Math.random() < 0.5 ? new MyFish(4) : new MyBird("fly Bird");
  18. }
  19. // 随机选一个类型,类型为SpaceRepeatingPadder | StringPadder
  20. let padder: Animal = getRandomAnimal();
  21. if (padder instanceof MyFish) {
  22. const name = padder.name(); // 类型细化为'SpaceRepeatingPadder'
  23. console.log("如果是MyFish===》", name);
  24. }
  25. if (padder instanceof MyBird) {
  26. const name = padder.name(); // 类型细化为'StringPadder'
  27. console.log("如果是MyBird===》", name);
  28. }

三、可以为null或undefined的类型

类型检查器认为 null与 undefined可以赋值给任何类型。 null与 undefined是所有其它类型的一个有效值。

  1. // 注意,按照JavaScript的语义,TypeScript会把 null和 undefined区别对待。 string | null, string | undefined和 string | undefined | null是不同的类型。
  2. let s = "foo";
  3. // s = null; // 错误, 'null'不能赋值给'string'
  4. let sn: string | null = "bar";
  5. sn = null; // 可以
  6. // sn = undefined; // 错误, 'undefined'不能赋值给'string | null'

1,可选参数和可选属性
a,可选参数会被自动地加上 | undefined:

  1. const add = (x: number, y?: number) => {
  2. return x + (y || 0);
  3. };
  4. add(1, 2);
  5. add(1);
  6. add(1, undefined); // 直接设置undefined
  7. // add(1, null); // 错误, 'null' 不能赋值给 'number | undefined'

b,可选属性也会有同样的处理:

  1. class MyClass {
  2. type?: number;
  3. }
  4. let myClass = new MyClass();
  5. myClass.type = 13;
  6. myClass.type = undefined; // ok
  7. // c.b = null; // 错误, 'null' 不属于 'number | undefined'

2,类型保护和类型断言
a,由于可以为null的类型是通过联合类型实现,那么你需要使用类型保护来去除 null

  1. const f1 = (sn: string | null): string => {
  2. if (sn == null) {
  3. return "default";
  4. } else {
  5. return sn;
  6. }
  7. };
  8. f1(null);
  9. const f2 = (sn: string | undefined): string => {
  10. return sn || "default";
  11. };

b,如果编译器不能够去除 null或 undefined,你可以使用类型断言手动去除。 语法是添加 !后缀: identifier!从 identifier的类型里去除了 null和 undefined:

  1. const broken = (name: string | null): string => {
  2. const postfix = (epithet: string) => {
  3. //return name.charAt(0) + '. the ' + epithet; // 错误, 'name' 可能是 null
  4. return name!.charAt(0) + ". the " + epithet; // 正确
  5. };
  6. name = name || "Bob";
  7. return postfix("great");
  8. };