类型保护

  • 类型保护就是一些表达式,它们在编译的时候就能通过类型信息确保某个作用域内变量的类型。
  • 类型保护就是能够通过关键字判断分支中的类型。
  • 类型保护,主要是靠js的特性和ts自带的功能来实现的。

typeof

判断 变量的类型 来进行保护。

  1. function fn(val: string|number){
  2. if (typeof val === 'string'){
  3. val // string
  4. } else{
  5. val // number
  6. }
  7. }

instanceof

判断 谁是谁的实例 来进行保护。

  1. class Person {}
  2. class Dog {}
  3. const createClass = (clazz: {new():Person|Dog}) => {
  4. return new clazz();
  5. }
  6. let r = createClass(Person);
  7. if (r instanceof Person){
  8. r //Person
  9. } else {
  10. r //Dog
  11. }

in 操作符

判断 谁是谁的属性 来进行保护。

  1. interface Fish{
  2. swiming: string;
  3. }
  4. interface Bird{
  5. fly: string;
  6. }
  7. function getAnimalType(animal: Fish|Bird){
  8. if ('swiming' in animal){
  9. animal //鱼
  10. } else{
  11. animal //鸟
  12. }
  13. }

null 保护

如果开启了 strictNullChecks 选项,那么对于可能为 null 的变量不能调用它上面的方法和属性

  1. function getFirstLetter(s:string|null){
  2. // 第一种方法:加上null判断
  3. if (s === null) return '';
  4. // 第二种方法:增加一个或的处理
  5. s = s || ''; //ts就会明确出来它是字符串
  6. return s.charAt(0);
  7. }
  8. // 它并不能处理一些复杂的判断,需要加非空断言操作符
  9. function getFirstLetter2(s:string|null){
  10. s = s || ''; //ts就会明确出来它是字符串
  11. function log(){
  12. // 虽然函数外层作用域,ts已经明确是字符串了,但是内层函数,ts仍然不会认为它是个字符串
  13. // 因此仍需要做处理
  14. // if (s !== null) console.log(s!.trim()); //方法1
  15. console.log(s!.trim()); //方法2:增加非空断言操作符
  16. }
  17. log();
  18. return s.charAt(0);
  19. }

链判断运算符(?.

  • 链判断运算符是一种先检查属性是否存在,再尝试访问该属性的运算符,其符号为 ?.
  • 如果运算符左侧的操作数 ?. 计算为 undefiend 或 null,则表达式求值为 undefiend。否则,正常触发目标属性访问,方法或函数调用。 ```typescript //如果 a 是 null/undefined,那么返回 undefined,否则返回 a.b 的值. a?.b; a == null ? undefined : a.b;

//如果 a 是 null/undefined,那么返回 undefined,否则返回 a[x] 的值 a?.[x]; a == null ? undefined : a[x];

// 如果 a 是 null/undefined,那么返回 undefined a?.b(); a == null ? undefined : a.b(); //如果a.b不函数的话抛类型错误异常,否则计算a.b()的结果

//如果 a 是 null/undefined,那么返回 undefined a?.(); a == null ? undefined : a(); //如果A不是函数会抛出类型错误,否则 调用 a 这个函数

  1. > 目前还处于 stage1 阶段,TS暂时也不支持
  2. <a name="YIxnf"></a>
  3. ## 可辨识的联合类型
  4. - 利用联合类型中的共有字段进行类型保护的一种技巧
  5. - 相同字段的不同取值就是可辨识
  6. ```typescript
  7. interface WarningButton{
  8. type: 'warning', //增加一个字面量类型,通过它们的不同取值,进行判断。这就是可辨识
  9. class: string,
  10. }
  11. interface DangerButton{
  12. type: 'danger',
  13. class: string,
  14. }
  15. function getButton(button: WarningButton|DangerButton){
  16. if (button.type === 'warning'){
  17. button //WarningButton
  18. }
  19. if (button.type === 'danger'){
  20. button //DangerButton
  21. }
  22. }

类型字面量 + 可辨识联合类型

  1. interface User{
  2. username:string;
  3. }
  4. type Action = {type: 'add', payload:User}|{type: 'delete', payload:number};
  5. const UserReducer = (action:Action) => {
  6. switch (action.type) {
  7. case 'add':
  8. let user:User = action.payload;
  9. break;
  10. case 'delete':
  11. let id:number = action.payload;
  12. break;
  13. default:
  14. break;
  15. }
  16. }

自定义的类型保护(is)

  • Ts 里的类型保护本质上就是一些表达式,它们在运行时检查类型信息,以确保在某个作用域里的类型是符合预期的
  • type is Type1 就是类型谓词
  • 谓词为 parameterName is Type 这种形式,parameterName 必须是来自于当前函数签名里的一个参数名
  • 每当使用一些变量调用 isType1 时,如果原始类型兼容,TypeScript 会将该变量缩小到该特定类型
    1. // 自定义一个类型保护函数(判断 函数参数type 是不是 类型Type1)
    2. function isType1(type: Type1|Type2):type1 is Type1{
    3. return (type as Type1).func1 !== undefined;
    4. }
    示例1:
    1. namespace a{
    2. function isString(val:any){
    3. return Object.prototype.toString.call(val) === '[object String]';
    4. }
    5. const str = 1;
    6. if (isString(str)){
    7. str //number
    8. }
    9. }
    10. namespace b{
    11. // “val is string” 根据函数的返回值确定是不是string类型。
    12. // 如果返回值是true,就认为它是string类型;如果返回值是false,就认为它不是string类型。
    13. function isString(val:any): val is string{
    14. return Object.prototype.toString.call(val) === '[object String]';
    15. }
    16. const str = 1;
    17. if (isString(str)){
    18. str //never
    19. }
    20. }

示例2:
  1. interface Bird {
  2. swing:number; //2
  3. }
  4. interface Dog{
  5. leg:number; //4
  6. }
  7. // 没有相同字段,可以自定义一个类型保护函数
  8. // parameterName is Type 这种形式就是类型谓词,那个函数参数是什么类型
  9. // parameterName 必须是来自于当前函数签名里的一个参数名
  10. function isBird(x:Bird|Dog): x is Bird{
  11. return (x as Bird).swing == 2; //如果x的swing属性不为2,就认为不是Bird类型
  12. }
  13. function getAnimal(x:Bird|Dog){
  14. if (isBird(x)){
  15. return `Bird => ${x.swing}`;
  16. }
  17. return `Dog => ${x.leg}`;
  18. }
  19. console.log(getAnimal({ swing: 2 })); //Bird => 2
  20. console.log(getAnimal({ swing: 3 })); //Dog => undefined
  21. console.log(getAnimal({ leg: 4 })); //Dog => 4

代码的完整性保护

  • 主要靠的是 never ,利用 never 无法到达最终结果的特性,来保护代码的完整 ```typescript interface ISquare{ type: ‘square’, width: number, }

interface IRant{ type: ‘rant’, width: number, height: number, } interface ICircle{ type: ‘circle’, r: number, }

const assert = (obj:never) => {throw new Error(‘err’)};

function getArea(obj: ISquare|IRant|ICircle){ switch (obj.type){ case ‘square’: return obj.width obj.width; break; case ‘rant’: return obj.width obj.height; break; // case ‘circle’: // return Math.PIobj.robj.r; // break; default: assert(obj); //完整性保护,加上后,下面的circle就提示报错了。 } }

getArea({type: ‘circle’, r: 10});

  1. <a name="Kdh9J"></a>
  2. # unknown 类型
  3. - ts3.0 引入了新的 unknown 类型,它是 any 类型对应的安全类型
  4. - unknown 和 any 的主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操作之前, 我们必须进行某种形式的检查。而在对 any 类型的值执行操作之前,我们不必进行任何检查。
  5. - unknown 不能通过属性变量取值。`let r:unknown = 1`
  6. <a name="I1tlf"></a>
  7. ## any 类型
  8. - 在 ts 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的 顶级类型(也称为 全局超级类型)
  9. - ts 允许我们对 any 类型的值执行任何操作,而无需事先执行任何形式的检查
  10. ```typescript
  11. let value:any;
  12. value = true; //ok
  13. value = 1; //ok
  14. value = 'hello'; //ok
  15. value = []; //ok
  16. value = {}; //ok
  17. value = Math.random; //ok
  18. value = null; //ok
  19. value = undefined; //ok
  20. value = new TypeError(); //ok
  21. let x:any;
  22. x.length; //ok
  23. x.foo.bar; //ok
  24. x.foo(); //ok
  25. x(); //ok
  26. new x(); //ok

unknown 类型

  • 所有类型也都可以被归为 unknown 类型。这使得 unknown 成为 ts 类型系统的另一种顶级类型

任何类型都可以赋值给 unknown 类型,也可以在上面进行任何操作。
  1. let value:unknown;
  2. value = true; //ok
  3. value = 1; //ok
  4. value = 'hello'; //ok
  5. value = []; //ok
  6. value = {}; //ok
  7. value = Math.random; //ok
  8. value = null; //ok
  9. value = undefined; //ok
  10. value = new TypeError(); //ok
  11. //可以在 any 上面进行任何操作
  12. let x:any;
  13. x.length; //ok
  14. x.foo.bar; //ok
  15. x.foo(); //ok
  16. x(); //ok
  17. new x(); //ok

unknown 类型只能被赋值给 any 类型和 unknown 类型本身,不能在 unknown 上面进行任何操作
  1. let value:unknown;
  2. let x1:unknown = value; //ok
  3. let x2:any = value; //ok
  4. let x3:boolean = value; //error
  5. let x4:number = value; //error
  6. let x5:string = value; //error
  7. let x6:object = value; //error
  8. let x7:any[] = value; //error
  9. let x8:Function = value; //error
  10. //不能在unknown上面进行任何操作
  11. let x:unknown;
  12. x.length; //error
  13. x.foo; //error
  14. x.foo(); //error
  15. x(); //error
  16. new x(); //error

缩小 unknown 类型范围

  • 如果没有类型断言或者类型细化时,不能在 unknown 上面进行任何操作
  • typeof
  • instanceof
  • 自定义类型保护函数
  • 可以对 unknown 类型使用类型断言 ```typescript let x:unknown;

// 断言 (x as string).length; let x1:number = x as number;

// typeof if (typeof x === ‘string’){ x.length; }

  1. <a name="ptCYE"></a>
  2. ## 联合类型中的 unknown 类型
  3. 在联合类型中,unknown 类型会吸收任何类型。这就意味着如果任一组成类型是 unknown,联合类型也会相当于 unknown。
  4. ```typescript
  5. type UnionType1 = unknown | null; // unknown
  6. type UnionType2 = unknown | undefined; // unknown
  7. type UnionType3 = unknown | string; // unknown
  8. type UnionType4 = unknown | number[]; // unknown

交叉类型中的 unknown 类型

在交叉类型中,任何类型都可以吸收 unknown 类型。这意味着将任何类型与 unknown 相交不会改变结果类型。

  1. type IntersectionType1 = unknown & null; // null
  2. type IntersectionType2 = unknown & undefined; // undefined
  3. type IntersectionType3 = unknown & string; // string
  4. type IntersectionType4 = unknown & number[]; // number[]
  5. type IntersectionType5 = unknown & any; // any

never 是unknown的子类型

  1. type isNever = never extends unknown ? true : false;

keyof unknown 等于 never

  1. type key = keyof unknown;

只能对unknown进行等或不等操作,不能进行其它任何操作

  1. let a:unknown;
  2. let b:unknown;
  3. console.log(a === b); //true 可以进行 等 操作
  4. console.log(a !== b); //false 可以进行 不等 操作
  5. console.log(a + b); //error 不可以进行其它操作
  6. console.log(a += b); //error
  7. // 不能访问属性
  8. a.name;
  9. // 不能作为函数调用
  10. a();
  11. // 不能当作类的构造函数,不能创建实例
  12. new a();

映射属性

如果映射类型遍历的时候是 unknown,不会映射属性

  1. type getType<T> = {
  2. [P in keyof T]:number
  3. }
  4. type t = getType<unknown>;