类型保护
- 类型保护就是一些表达式,它们在编译的时候就能通过类型信息确保某个作用域内变量的类型。
- 类型保护就是能够通过关键字判断分支中的类型。
- 类型保护,主要是靠js的特性和ts自带的功能来实现的。
typeof
判断 变量的类型 来进行保护。
function fn(val: string|number){if (typeof val === 'string'){val // string} else{val // number}}
instanceof
判断 谁是谁的实例 来进行保护。
class Person {}class Dog {}const createClass = (clazz: {new():Person|Dog}) => {return new clazz();}let r = createClass(Person);if (r instanceof Person){r //Person} else {r //Dog}
in 操作符
判断 谁是谁的属性 来进行保护。
interface Fish{swiming: string;}interface Bird{fly: string;}function getAnimalType(animal: Fish|Bird){if ('swiming' in animal){animal //鱼} else{animal //鸟}}
null 保护
如果开启了 strictNullChecks 选项,那么对于可能为 null 的变量不能调用它上面的方法和属性
function getFirstLetter(s:string|null){// 第一种方法:加上null判断if (s === null) return '';// 第二种方法:增加一个或的处理s = s || ''; //ts就会明确出来它是字符串return s.charAt(0);}// 它并不能处理一些复杂的判断,需要加非空断言操作符function getFirstLetter2(s:string|null){s = s || ''; //ts就会明确出来它是字符串function log(){// 虽然函数外层作用域,ts已经明确是字符串了,但是内层函数,ts仍然不会认为它是个字符串// 因此仍需要做处理// if (s !== null) console.log(s!.trim()); //方法1console.log(s!.trim()); //方法2:增加非空断言操作符}log();return s.charAt(0);}
链判断运算符(?.)
- 链判断运算符是一种先检查属性是否存在,再尝试访问该属性的运算符,其符号为
?. - 如果运算符左侧的操作数 ?. 计算为 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 这个函数
> 目前还处于 stage1 阶段,TS暂时也不支持<a name="YIxnf"></a>## 可辨识的联合类型- 利用联合类型中的共有字段进行类型保护的一种技巧- 相同字段的不同取值就是可辨识```typescriptinterface WarningButton{type: 'warning', //增加一个字面量类型,通过它们的不同取值,进行判断。这就是可辨识class: string,}interface DangerButton{type: 'danger',class: string,}function getButton(button: WarningButton|DangerButton){if (button.type === 'warning'){button //WarningButton}if (button.type === 'danger'){button //DangerButton}}
类型字面量 + 可辨识联合类型
interface User{username:string;}type Action = {type: 'add', payload:User}|{type: 'delete', payload:number};const UserReducer = (action:Action) => {switch (action.type) {case 'add':let user:User = action.payload;break;case 'delete':let id:number = action.payload;break;default:break;}}
自定义的类型保护(is)
- Ts 里的类型保护本质上就是一些表达式,它们在运行时检查类型信息,以确保在某个作用域里的类型是符合预期的
type is Type1就是类型谓词- 谓词为
parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名 - 每当使用一些变量调用
isType1时,如果原始类型兼容,TypeScript会将该变量缩小到该特定类型// 自定义一个类型保护函数(判断 函数参数type 是不是 类型Type1)function isType1(type: Type1|Type2):type1 is Type1{return (type as Type1).func1 !== undefined;}
示例1:
namespace a{function isString(val:any){return Object.prototype.toString.call(val) === '[object String]';}const str = 1;if (isString(str)){str //number}}namespace b{// “val is string” 根据函数的返回值确定是不是string类型。// 如果返回值是true,就认为它是string类型;如果返回值是false,就认为它不是string类型。function isString(val:any): val is string{return Object.prototype.toString.call(val) === '[object String]';}const str = 1;if (isString(str)){str //never}}
示例2:
interface Bird {swing:number; //2}interface Dog{leg:number; //4}// 没有相同字段,可以自定义一个类型保护函数// parameterName is Type 这种形式就是类型谓词,那个函数参数是什么类型// parameterName 必须是来自于当前函数签名里的一个参数名function isBird(x:Bird|Dog): x is Bird{return (x as Bird).swing == 2; //如果x的swing属性不为2,就认为不是Bird类型}function getAnimal(x:Bird|Dog){if (isBird(x)){return `Bird => ${x.swing}`;}return `Dog => ${x.leg}`;}console.log(getAnimal({ swing: 2 })); //Bird => 2console.log(getAnimal({ swing: 3 })); //Dog => undefinedconsole.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});
<a name="Kdh9J"></a># unknown 类型- ts3.0 引入了新的 unknown 类型,它是 any 类型对应的安全类型- unknown 和 any 的主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操作之前, 我们必须进行某种形式的检查。而在对 any 类型的值执行操作之前,我们不必进行任何检查。- unknown 不能通过属性变量取值。`let r:unknown = 1`<a name="I1tlf"></a>## any 类型- 在 ts 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的 顶级类型(也称为 全局超级类型)- ts 允许我们对 any 类型的值执行任何操作,而无需事先执行任何形式的检查```typescriptlet value:any;value = true; //okvalue = 1; //okvalue = 'hello'; //okvalue = []; //okvalue = {}; //okvalue = Math.random; //okvalue = null; //okvalue = undefined; //okvalue = new TypeError(); //oklet x:any;x.length; //okx.foo.bar; //okx.foo(); //okx(); //oknew x(); //ok
unknown 类型
- 所有类型也都可以被归为 unknown 类型。这使得 unknown 成为 ts 类型系统的另一种顶级类型
任何类型都可以赋值给 unknown 类型,也可以在上面进行任何操作。
let value:unknown;value = true; //okvalue = 1; //okvalue = 'hello'; //okvalue = []; //okvalue = {}; //okvalue = Math.random; //okvalue = null; //okvalue = undefined; //okvalue = new TypeError(); //ok//可以在 any 上面进行任何操作let x:any;x.length; //okx.foo.bar; //okx.foo(); //okx(); //oknew x(); //ok
unknown 类型只能被赋值给 any 类型和 unknown 类型本身,不能在 unknown 上面进行任何操作
let value:unknown;let x1:unknown = value; //oklet x2:any = value; //oklet x3:boolean = value; //errorlet x4:number = value; //errorlet x5:string = value; //errorlet x6:object = value; //errorlet x7:any[] = value; //errorlet x8:Function = value; //error//不能在unknown上面进行任何操作let x:unknown;x.length; //errorx.foo; //errorx.foo(); //errorx(); //errornew 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; }
<a name="ptCYE"></a>## 联合类型中的 unknown 类型在联合类型中,unknown 类型会吸收任何类型。这就意味着如果任一组成类型是 unknown,联合类型也会相当于 unknown。```typescripttype UnionType1 = unknown | null; // unknowntype UnionType2 = unknown | undefined; // unknowntype UnionType3 = unknown | string; // unknowntype UnionType4 = unknown | number[]; // unknown
交叉类型中的 unknown 类型
在交叉类型中,任何类型都可以吸收 unknown 类型。这意味着将任何类型与 unknown 相交不会改变结果类型。
type IntersectionType1 = unknown & null; // nulltype IntersectionType2 = unknown & undefined; // undefinedtype IntersectionType3 = unknown & string; // stringtype IntersectionType4 = unknown & number[]; // number[]type IntersectionType5 = unknown & any; // any
never 是unknown的子类型
type isNever = never extends unknown ? true : false;
keyof unknown 等于 never
type key = keyof unknown;
只能对unknown进行等或不等操作,不能进行其它任何操作
let a:unknown;let b:unknown;console.log(a === b); //true 可以进行 等 操作console.log(a !== b); //false 可以进行 不等 操作console.log(a + b); //error 不可以进行其它操作console.log(a += b); //error// 不能访问属性a.name;// 不能作为函数调用a();// 不能当作类的构造函数,不能创建实例new a();
映射属性
如果映射类型遍历的时候是 unknown,不会映射属性
type getType<T> = {[P in keyof T]:number}type t = getType<unknown>;
