Typescript ---类型保护 类型谓词

4,005 阅读2分钟

在TS中,我们可以使用 inkeyofintanceof 来进行类型保护

in

interface Admin {
  name: string;
  privileges: string[];
}

interface Employee {
  name: string;
  startDate: Date;
}

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
  console.log("Name: " + emp.name);
  if ("privileges" in emp) {
    console.log("Privileges: " + emp.privileges);
  }
  if ("startDate" in emp) {
    console.log("Start Date: " + emp.startDate);
  }
}

keyof

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 Padder {
  getPaddingString(): string;
}

class SpaceRepeatingPadder implements Padder {
  constructor(private numSpaces: number) {}
  getPaddingString() {
    return Array(this.numSpaces + 1).join(" ");
  }
}

class StringPadder implements Padder {
  constructor(private value: string) {}
  getPaddingString() {
    return this.value;
  }
}

let padder: Padder = new SpaceRepeatingPadder(6);

if (padder instanceof SpaceRepeatingPadder) {
  // padder的类型收窄为 'SpaceRepeatingPadder'
}

类型谓词 is

先来看一个简单的应用

function isNumber(x: any): x is number {
  return typeof x === "number";
}

function isString(x: any): x is string {
  return typeof x === "string";
}

通过 x is type 来指定x的类型 相当于调用上面的函数 如果返回为true 那么 x 类型就是number

为什么会有类型保护这个东西呢? 在js中,有时候我们去访问对象的某个属性,但是可能这个属性不存在,那么就会为undefind,有时候会不符合我们的预期。当然你可以用过配置bable配置可选链保证某个对象是有属性的(使用过angular的同学应该会比较清楚)。在TS中,我们必须明确的指定对象的哪些属性是否存在.

	// 定义一个动物的接口
	interface Animal {
		eat:() => void 
	}

class Dog extends Animal {
	eat(){
    	console.log('dog eat')
    }
    jump(){
    	console.log('jump')
    }
}

// 定义一个类来使用动物
class UseAnimal {
        constructor(public animal :Animal)
	}

const dog = new Dog()
const dogAnimal = new UseAnimal(dog)
const { animal } = dogAnimal

此时得到的animale实例只有一个eat方法 如果想要调用上面的jump方法 就需要告诉ts animal是Dog的实例

if(animal instanceof Dog) {
	animal.jump()
}

但是这个方案有个具象性,即它只对有效果。例如我们重新声明一个类

class Cat extends Animal {
	eat(){
    	console.log('dog eat')
    }
    jump(){
    	console.log('jump')
    }
}
const cat = new Cat()
const catAnimal = new UseAnimal(cat)
const { animal } = catAnimal
if(animal instanceof Dog) {
	animal.jump()
}

Cat 和 Dog 是两个一样的类 但是 if(animal instanceof Dog) {animal.jump()}这个判断就会返回为false 因为cat不是dogAnimal的实例 这时候你不得不去手动的修改判断

** 因此我们可以用类型谓词 **

function isAnimal(animal:any) :anmale is Dog {
	return (animal as Dog).jump !== undefined
}

// 因此上面的判断可以改写为
if(isAnimal(animal)){
	animal.jump() // 这样就不会报错了
}

但是上面这种写法需要为每一种类型去写判断函数。 因此我们可以利用泛型才重新定义函数

	function isAnimal<T>(toCheck:any,toCheckProperty:keyof T):toCheck is T {
    	return (toCheck as T)[toCheckProperty] !== undefind
    }