05.TypeScript 类型断言 、联合类型 、 交叉类型

59 阅读3分钟

05.TypeScript 类型断言 、联合类型 、 交叉类型

1. 类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型。

1.1 语法

类型断言有两种形式:

  1. "尖括号"语法:<类型>值
  2. as 语法:值 as 类型

1.2 实例

// 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// 我们明确告诉编译器 someValue 是一个字符串类型,因此可以访问其 length 属性
console.log(strLength); // 输出: 16

// as 语法
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;

// 使用 as 语法同样明确告诉编译器 someValue2 是一个字符串类型
console.log(strLength2); // 输出: 16

1.3 非空断言操作符(!)

在上下文中当类型检查器无法断定类型时,可以使用 ! 进行断言操作符来手动指定其类型不为 null 和 undefined。

function getLength(s: string | null): number {
  return s!.length; // 断言 s 不为 null
}

// 我们明确告诉编译器 s 不会是 null,因此可以直接访问其 length 属性
console.log(getLength("hello")); // 输出: 5

// 如果传入 null,运行时会报错
// console.log(getLength(null)); // 运行时错误

2. 联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。

2.1 实例

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

// myFavoriteNumber 可以是字符串或数字类型,因此两种赋值都有效
console.log(myFavoriteNumber); // 输出: 7

// 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,
// 我们只能访问此联合类型的所有类型里共有的属性或方法
function getLength2(something: string | number): number {
  // return something.length; // 错误: number 类型没有 length 属性
  return something.toString().length; // 正确: toString() 是 string 和 number 的共有方法
}

// 由于 number 类型没有 length 属性,我们需要先转换为字符串再获取长度
console.log(getLength2("hello")); // 输出: 5
console.log(getLength2(123)); // 输出: 3

3. 交叉类型

交叉类型(Intersection Types)将多个类型合并为一个类型,包含了所需的所有类型的特性。

3.1 实例

interface IAnyObject {
  [propName: string]: any;
}

interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

// 交叉类型
const pet: Bird & Fish = {
  fly() {
    console.log('Bird is flying');
  },
  swim() {
    console.log('Fish is swimming');
  },
  layEggs() {
    console.log('Laying eggs');
  }
};

// pet 对象同时具有 Bird 和 Fish 接口的所有方法
pet.fly(); // 输出: Bird is flying
pet.swim(); // 输出: Fish is swimming
pet.layEggs(); // 输出: Laying eggs

输入图片说明

4. 类型守卫

类型守卫可以帮助我们在运行时检查变量的类型。

4.1 typeof 类型守卫

function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return " ".repeat(padding) + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

// 当 padding 是数字时,我们重复空格字符串
console.log(padLeft("Hello world", 4)); // 输出: "    Hello world"

// 当 padding 是字符串时,我们直接拼接字符串
console.log(padLeft("Hello world", "---")); // 输出: "---Hello world"

4.2 instanceof 类型守卫

class Bird {
  fly() {
    console.log("Bird is flying");
  }
  layEggs() {
    console.log("Laying eggs");
  }
}

class Fish {
  swim() {
    console.log("Fish is swimming");
  }
  layEggs() {
    console.log("Laying eggs");
  }
}

function move(animal: Bird | Fish) {
  if (animal instanceof Bird) {
    // 通过 instanceof 确定 animal 是 Bird 类型,可以调用 fly 方法
    animal.fly(); // 输出: Bird is flying
  } else if (animal instanceof Fish) {
    // 通过 instanceof 确定 animal 是 Fish 类型,可以调用 swim 方法
    animal.swim(); // 输出: Fish is swimming
  }
}

move(new Bird()); // 输出: Bird is flying
move(new Fish()); // 输出: Fish is swimming

5. 类型别名

类型别名用来给一个类型起个新名字。

5.1 实例

// 基本类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

function getName(n: NameOrResolver): Name {
  if (typeof n === 'string') {
    return n;
  } else {
    return n();
  }
}

// 传入字符串时直接返回
console.log(getName("Alice")); // 输出: Alice

// 传入函数时调用函数返回结果
console.log(getName(() => "Bob")); // 输出: Bob

// 字符串字面量类型别名
type EventNames = 'click' | 'scroll' | 'mousemove';

function handleEvent(ele: Element, event: EventNames) {
  // 处理事件
  console.log(`Handling ${event} event`);
}

// handleEvent(document.getElementById('hello'), 'click'); // 正确
// handleEvent(document.getElementById('hello'), 'dbclick'); // 错误: dbclick 不是 EventNames 类型

// EventNames 限制了 event 参数只能是 'click'、'scroll' 或 'mousemove' 中的一个
handleEvent(document.getElementById('hello')!, 'click'); // 输出: Handling click event

输入图片说明

6. 总结

  1. 类型断言允许我们手动指定值的类型,帮助编译器理解我们的意图;
  2. 联合类型表示取值可以为多种类型中的一种,增强了类型的灵活性;
  3. 交叉类型将多个类型合并为一个类型,包含了所需的所有类型的特性;
  4. 类型守卫帮助在运行时检查变量的类型,提高代码的安全性;
  5. 类型别名为类型起新名字,提高代码可读性和维护性。