TypeScript类型窄化
前端铁蛋儿 人气:0前言
TS最好用的地方就是强类型,随之而来的就是类型窄化,摸鱼的时候顺道总结下.
类型推论
TypeScript里,在有些没有明确指出类型的地方,类型推论会帮助提供类型
Example:
let x = [0, 1, null] // number let x = Math.random() < 0.5 ? 100 : "helloword" // number|string let x: Animal[] = [new Rhino(), new Elephant(), new Snake()]; // Rhino | Elephant | Snake
如果没有找到最佳通用类型的话,类型推断的结果为联合数组类型
联合类型和类型守卫
Example:
// 联合类型 type Types = number | string function typeFn(type: Types, input: string): string { // 如果这样写就需要判断type的类型 }
可以直接赋值类型推断
let x:number|string = 1 x="tiedan"
如果不判断会报错
function typeFn(type: number | string, input: string) { // 报错 运算符+号不能应用于 string return new Array(type + 1).join("") + input }
所以还得判断
function typeFn(type: number | string, input: string) { // 类型守卫 if (typeof type === 'number') { return new Array(type + 1).join(" ") + input } return type + input }
类型的窄化就是根据判断类型重新定义更具体的类型
那么问题来了学这玩意干嘛? js不香吗?
个人观点:
使用 TypeScript 可以帮你降低 JavaScript 弱语言的脆弱性,帮你减少由于不正确类型导致错误产生的风险,以及各种 JavaScript 版本混杂造成错误的风险。
TypeScript 只是把高级语言的强类型这个最主要的特征引入 JavaScript ,就解决了防止我们在编写 JavaScript 代码时因为数据类型的转换造成的意想不到的错误,增加了我们排查问题的困难性。
typeof的类型守卫:
"string" "number" "bigint" // ES10新增 "boolean" "symbol" // ES6新增 "undefined" "object" "function"
注意: typeof null 等于 object
因此:
function strOrName(str: string | string[] | null) { if (typeof str === 'object') { for (const s of str) { // 报错 因为str有可能是null console.log(s) } } else if (typeof str === 'string') { console.log(str) } else { //...... } }
真值窄化
js的真值表很复杂, 除以下的是false其余的都是真。
0 NAN "" 0n // 0的bigint版本 null undefined
避免null的错误可以利用真值窄化
// 利用真值判断 if (str && typeof strs === 'object') { for (const s of strs) { console.log(s) } }
或者这样也行
function valOrName(values: number[] | undefined, filter: number): number[] | undefined { if (!values) { return values } else { return values.filter(x => x > filter) } }
小结: 真值窄化帮助我们更好的处理null/undefined/0 等值
相等性窄化
想等性窄化就是利用 ===、 !== 、==、and、!= 等运算符进行窄化
Example1:
function strOrNum(x: string | number, y: string | boolean) { if (x === y) { // string } else { // string|number } }
Example2:
function strOrName(str: string | string[] | null) { if (str !== null) { if (typeof str === 'object') { for (const s of str) { console.log(s) // [] } } else if (typeof str === 'string') { console.log(str) // string } else { // ..... } } }
Example3:
interface Types { value: number | null | undefined } function valOrType(type: Types, val: number) { // null和undefined 都是false 只能是number if (type.value != null) { type.value *= val } }
in操作符窄化
in是检查对象中是否有属性,现在充当了一个 "type guard" 的角色。
Example:
interface A { a: number }; interface B { b: string }; function foo(x: A | B) { if ("a" in x) { return x.a; } return x.b; }
instanceof窄化
instanceof表达式的右侧必须属于类型 any,或属于可分配给 Function接口类型的类型。
Example:
function dateInval(x: Date | string) { if (x instanceof Date) { // Date } else { // string } }
窄化的本质
窄化的本质是重新定义类型
Example:
function example() { let x: string | number | boolean x = Math.random() < 0.5 if (Math.random() < 0.5) { x = 'hello' // string } else { x = 100 // number } return x; // string|number }
联合类型的窄化
Example1:
interface Shape { kind: "cirle" | "square", redius?: number sideLength?: number } // 报错 function getArea(shape: Shape) { return Math.PI * shape.redius ** 2 } // 窄化还是报错 function getArea(shape: Shape) { if (shape.kind === 'cirle') { return Math.PI * shape.redius ** 2 } } // 利用非空断言阔以 function getArea(shape: Shape) { if (shape.kind === 'cirle') { return Math.PI * shape.redius! ** 2 } }
Example2:
interface Circle { kind: "cirle"; redius: number; } interface Square { kind: "square"; redius: number; } type Shape = Circle | Square function getArea(shape: Shape) { if (shape.kind === 'cirle') { // 窄化 return Math.PI * shape.redius ** 2 } } // 或者 function getArea(shape: Shape) { switch (shape.kind) { case "cirle": return Math.PI * shape.redius ** 2 case "square": return shape.sideLength ** 2 default: const _example: never = shape return _example } }
总结
加载全部内容