文章目录

  • 类型保护
  • typeof
  • instanceof
  • in
  • 字面量类型保护
  • 自定义类型保护
  • 类型操作
  • typeof
  • keyof 获取类型的所有 key 的集合
  • in
  • 类型兼容
  • 缺少的方法会报错,不兼容
  • 多余的方法会兼容
  • 接口实现不存在的方法


类型保护

我们通常在 JavaScript 中通过判断来处理⼀些逻辑
在 TypeScript 中这种条件语句块还有另外⼀个特性:
根据判断逻辑的结果,缩⼩类型范围(有点类似断⾔),这种特性称为 类型保护 ,触发条件:

  • 逻辑条件语句块:if、else、elseif
  • 特定的⼀些关键字:typeof、instanceof、in……

typeof

function fn(a: string | number) {
    // error,不能保证 a 就是字符串,number上没有substring属性
    a.substring(1);
    if (typeof a === 'string') {
        // ok
        a.substring(1);
    } else {
        // ok
        a.toFixed(1);
    }
}

不能保证 a 就是字符串,number上没有substring属性

instanceof

与 typeof 类似的, instanceof 也可以被 TypeScript 识别为类型保护

for of 添加typescript类型 typescript instanceof_自定义类型

in

for of 添加typescript类型 typescript instanceof_数据_02

字面量类型保护

如果类型为字⾯量类型,那么还可以通过该字⾯量类型的字⾯值进⾏推断

for of 添加typescript类型 typescript instanceof_字符串_03

自定义类型保护

data is Element[]|NodeList 是⼀种类型谓词,格式为: xx is XX ,返回这种类型的函数就可以被 TypeScript 识别为类型保护

// 能否使用forEach?
function canEach(data: any): data is Element[] | NodeList {
    return data.forEach !== undefined;
}
function fn2(elements: Element[] | NodeList | Element) {

    if (canEach(elements)) {
        elements.forEach((el: Element) => {
            el.classList.add('box');
        });
    } else {
        elements.classList.add('box');
    }
}

类型操作

TypeScript 提供了⼀些⽅式来操作类型这种数据
但是需要注意的是,类型数据只能作为类型来使⽤,⽽不能作为程序中的数据
这是两种不同的数据,⼀个⽤在编译检测阶段⼀个⽤于程序执⾏阶段

typeof

在 TypeScript 中, typeof 有两种作⽤

  • 获取数据的类型
  • 捕获数据的类型
let str1 = 'lc';
// 如果是 let ,把 'string' 作为值
let t = typeof str1;
console.log(t)
// 如果是 type,把 'string' 作为类型
type myType = typeof str1;	// 仅仅用于程序检测,并不会编译成js
console.log(myType);
let str2: myType = 'lc';
let str3: typeof str1 = 'lc';

for of 添加typescript类型 typescript instanceof_自定义类型_04

keyof 获取类型的所有 key 的集合

interface Person {
    name: string;
    age: number;
};
type personKeys = keyof Person;
// 等同:type personKeys = "name" | "age"
let p1 = {
    name: 'lc',
    age: 18
}
function getPersonVal(k: personKeys) {
    return p1[k];
}
/**
等同:
function getPersonVal(k: 'name'|'age') {
    return p1[k];
}
*/
getPersonVal('name'); // 正确
getPersonVal('gender'); // 错误
let p1 = {
    name: "lc",
    age: 18
}
type PT = typeof p1;

function getPersonVal(k: keyof typeof p1) {
    return p1[k];
}

in

针对类型进⾏操作的话,内部使⽤的 for…in 对类型进⾏遍历

interface Person {
    name: string;
    age: number;
}
type personKeys = keyof Person;
type newPerson = {
    // 把类型全部变成number
    [k in personKeys]: number;
    /**
    等同 [k in 'name'|'age']: number;
    也可以写成
    [k in keyof Person]: number;
    */
}

注意: in 后⾯的类型值必须是 string 或者 number 或者 symbol

类型兼容

  • TypeScript 的类型系统是基于结构⼦类型的
  • 它与名义类型(如:java)不同(名义类型的数据类型兼容性或等价性是通过明确的声明或类型的名称来决定的)
  • 这种基于结构⼦类型的类型系统是基于组成结构的
  • 只要具有相同类型的成员,则两种类型即为兼容的。
class Person {
    name: string;
    age: number;
}
class Cat {
    name: string;
    age: number;
}
function fn(p: Person) {
    p.name;
}
let c1 = new Cat();
// ok,因为 Cat 类型的结构与 Person 类型的结构相似,所以它们是兼容的
fn(c1);

缺少的方法会报错,不兼容

for of 添加typescript类型 typescript instanceof_字符串_05

多余的方法会兼容

for of 添加typescript类型 typescript instanceof_数据_06

接口实现不存在的方法

interface IFly {
    fly(): void;
}

class Person implements IFly {
    name: string;
    age: number;
    study() { };
    fly() { };
}
class Cat implements IFly {
    name: string;
    age: number;
    catchMouse() { };
    fly() { };
}
function fn(p: Person) {
    p.name;
}

function fn2(arg: IFly) {
    arg.fly();
}

let p1 = new Person();
let c1 = new Cat();

fn2(p1)
fn2(c1);