学习TS(9) 类型保护

188 阅读2分钟

「这是我参与2022首次更文挑战的第38天,活动详情查看:2022首次更文挑战」。

类型保护

类型保护是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内。 换句话说,类型保护可以保证一个字符串是一个字符串,尽管它的值也可以是一个数值。类型保护与特性检测并不是完全不同,其主要思想是尝试检测属性、方法或原型,以确定如何处理值。目前主要有四种的方式来实现类型保护:

typeof 关键字

typeof 类型保护只支持两种形式:typeof val === "typename" 和 typeof val !== typename,"typename" 必须是 "number", "string", "boolean" 或 "symbol"。 但是 TypeScript 并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

function double(val: number | string) {
    if (typeof val === 'number') {
        val
    } else {
        val
    }
}


instanceof 关键字

class Cat { }
class Dog { }

const getInstance = (clazz: { new(): Cat | Dog }) => {
    return new clazz();
}
let r = getInstance(Cat);
if(r instanceof Cat){
    r
}else{
    r
}


in 关键字


interface Fish {
    swiming: string,
}
interface Bird {
    fly: string,
    leg: number
}
function getType(animal: Fish | Bird) {
    if ('swiming' in animal) {
        animal // Fish
    } else {
        animal // Bird
    }
}

可辨识联合类型

TypeScript 可辨识联合(Discriminated Unions)类型,也称为代数数据类型或标签联合类型。它包含 3 个要点:可辨识、联合类型和类型守卫。 这种类型的本质是结合联合类型和字面量类型的一种类型保护方法。如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。

interface WarningButton {
    class: 'warning'
}
interface DangerButton {
    class: 'danger'
}
function createButton(button: WarningButton | DangerButton) {
    if (button.class == 'warning') {
        button // WarningButton
    } else {
        button // DangerButton
    }
}


null保护

这里注意的是 TypeScript 无法检测内部函数变量的类型

const addPrefix = (num?: number) => {
    num = num || 1.1;
    function prefix(fix: string) {
        return fix + num?.toFixed()
    }
    return prefix('df');
}
console.log(addPrefix());

自定义类型保护

interface Fish {
    swiming: string,
}
interface Bird {
    fly: string,
    leg: number
}
function isBird(animal: Fish | Bird):animal is Bird {
    return 'swiming' in animal
}
function getAniaml (animal:Fish | Bird){
    if(isBird(animal)){
        animal
    }else{
        animal
    }
}


完整性保护

interface ICircle {
    kind: 'circle',
    r: number
}
interface IRant {
    kind: 'rant',
    width: number,
    height: number
}
interface ISquare {
    kind: 'square',
    width: number
}
type Area = ICircle | IRant | ISquare
const isAssertion = (obj: never) => { }
const getArea = (obj: Area) => {
    switch (obj.kind) {
        case 'circle':
            return 3.14 * obj.r ** 2
        default:
            return isAssertion(obj); // 必须实现所有逻辑
    }
}