本文主要记录书中关于TypeScript类型收缩的内容
本文主要内容如下
- 类型收缩的一些方法
- 条件判断
- 抛错误
- instanceof 和 in 属性检查
- “标签联合”或“可辨识联合”
- 类型收缩的失效示例
- 自定义类型保护
- 总结
类型收缩的方法
条件判断
const el = document.getElementById("foo"); if (el) { el; // 类型是HTMLElement } else { el; // 类型是 null }
如果 el 是 null,那么第一个分支的代码就不会执行,所以 TypeScript 能够在这个代码块中推断出一个更容易处理的类型。
抛错误
const el = document.getElementById("foo"); if(!el) throw new Error('未找到元素'); el.innerHTML = 'Hello World'; //HTMLElement
instanceof 和 in 属性检查
function contains (text:string,search:string|RegExp) { if(search instanceof RegExp) { // 类型是Regexp return !!search.test(text); } // 类型是 string return text.includes(search); }
interface A {a:number} interface A {a:number} interface B {b:number} function pickAB(ab:A|B) { if('a' in ab) { ab // 类型是A } else { ab // 类型是B } ab // 类型是A|B }
ps:原文中提到一个示例如下:
function contains(text:string,terms:string|string[]) { const termList = Array.isArray(terms) ? terms : [terms]; termList // 类型是 string[] }
这是通过内置函数来完成类型收缩的
“标签联合”或“可辨识联合”
interface UploadEvent {type:'upload',filename:string;content:string} interface DownloadEvent {type:'download',filename:string;} type AppEvent = UploadEvent|DownloadEvent; function handleEvent(e:AppEvent) { switch(e.type) { case "upload": e // 类型是UploadEvent break; case 'download': e // 类型是Download break; } }
帮助检查器收缩范围的常见方法是给他们加上一个明确的“标签”,这种模式称为 “标签联合” 或 “可辨识联合”,它在TypeScript中很常见。
类型收缩的失效示例
const el = document.querySelector('#foo'); if(typeof el === 'object') { el; // Element | null }
在 JavaScript 中,typeof null 也是 "object",所以这个类型收缩是不成立的,类似的还有如下这样:
function foo(x?:number|string|null) { if(!x) { x // string | number | null | undefined } }
自定义类型保护
function isInputElement(el:HTMLElement): el is HTMLInputElement { return 'value' in el; } function getElementContent(el:HTMLElement) { if(isInputElement(el)) { el // 类型是 HTMLInputElement; return el.value; } return el.textContent; // 类型是 HTMLElement }
这就是所谓的“自定义类型保护”,el is HTMLInputElement 作为返回值,如果函数返回true,它可以收缩类型的参数。
跨数组或对象的类型收缩
const students = ["小明", "小红", "小柔"]; // const people = ["小红", "小柔"].map((who) => students.find((n) => n === who)); //类型 (string | undefined)[] // 如果用filter过滤undefined,TypeScript 无法跟上filter的逻辑 // const people = ["小红", "小柔"] // .map((who) => students.find((n) => n === who)) // .filter((who) => who != undefined); // 类型仍然是 (string | undefined)[] // 但是使用类型保护就可以 function isDefined<T>(x:T|undefined):x is T { return x != undefined; } const people = ["小红", "小柔"] .map((who) => students.find((n) => n === who)) .filter(isDefined); // 类型是 string[]
总结
- 了解TypeScript如何根据条件和其他类型的控制流来收缩类型。
- 使用标记/可辨识类型或者自定义类型保护来收缩类型。