TypeScript 中的类型窄化
延续前几篇文章的思路,我们探讨了模板字面量类型和类型与模拟,接下来我们将深入剖析 TypeScript 中与类型相关的另一个主题。在本文中,我们将学习各种类型缩减方法。类型缩减是指将类型从不太精确的类型转换为更精确的类型的过程。
我们先从一个简单的函数开始:
function friends(input: string | number) {
// code here
}
上述函数可以接受数字或字符串作为参数。假设我们想根据input参数是数字还是字符串来执行不同的操作。在这种情况下,我们将使用 JavaScript 的类型守卫来检查它是字符串还是数字,如下所示:
function someFunc(input: string | number) {
if(typeof input === "string") {
// do something with the string
console.log("input is a string");
}
if(typeof input === "number") {
// do something with number
console.log("input is a number");
}
}
类型保护
在上面的例子中,我们使用了 JavaScript 的类型守卫来限定变量的类型input为数字或字符串。类型守卫用于检查变量是否属于特定类型,例如 `number` number、` stringstring`、object`undefined` 等。当使用类型守卫时,TypeScript 会期望该变量是指定类型,并根据此信息自动进行类型检查。
以下是可用的 JavaScript 类型保护机制列表:
细绳
if(typeof param === "string") {
// do something with string value
}
数字
if(typeof param === "number") {
// do something with number value
}
大银
if(typeof param === "bigint") {
// do something with bigint value
}
布尔值
if(typeof param === "boolean") {
// do something with boolean value
}
象征
if(typeof param === "symbol") {
// do something with symbol value
}
不明确的
if(typeof param === "undefined") {
// do something with undefined value
}
目的
if(typeof param === "object") {
// do something with object value
}
功能
if(typeof param === "function") {
// do something with the function
}
真理性缩小
在这种类型的筛选中,我们会在使用变量之前检查它是否为真值。当变量为真值时,TypeScript 会自动排除该变量为假值(例如 `true`undefined或`false` 等)的可能性null,从而优化条件判断。
例如,以下示例中,函数someFunction接受一个input,其类型为字符串或 undefined(即可选)。
function someFunction(x?: string) {
if(x) {
console.log(typeof x) // "string"
}
}
通过检查input* * 是否为真,**x 的类型变为字符串,否则为未定义。
平等范围缩小
如果两个变量相等,则它们的类型必须相同。如果一个变量的类型不精确(例如,`int`、` unknownint`any等),并且等于另一个类型精确的变量,则 TypeScript 会利用该信息来缩小第一个变量的类型范围。
考虑以下函数,该函数接受两个参数:x和y,其中x可以是字符串或数字,y是数字。当x的值等于y的值时, x的类型被推断为数字,否则为字符串。
function someFunction(x: string | number, y: number) {
if(x === y) {
// narrowed to number
console.log(typeof x) // number
} else {
// this is not narrowed
console.log(typeof x) // number or string
}
}
受歧视的工会
这种方法是创建一个对象,其中包含一个字面成员,该成员可用于区分两个不同的联合类型。我们来看一个计算不同形状(矩形和圆形)平方的函数示例。首先,我们需要定义矩形和圆形的类型。
type Rectangle = {
shape: "reactangle",
width: number;
height: number;
}
type Circle = {
shape: "circle"
radius: number;
}
从上述类型来看,每个对象都将具有形状字段,该字段可以是 `a`circle或 `b` rectangle。我们可以在函数中使用形状字段来计算面积,该函数接受 `a` 和 ` Rectangleb`的并集Circle,如下所示:
function calculateArea(shape: Rectangle | Circle) {
if(shape.shape === "reactangle") {
// you can only access the properties of reactangle and not circle
console.log("Area of reactangle: " + shape.height * shape.width);
}
if(shape.shape === "circle") {
// you can only access the properties of circle and not reactangle
console.log("Area of circle: " + 3.14 * shape.radius * shape.radius);
}
}
当shape字段为矩形时,您只能访问该Rectangle类型中可用的属性,即 `x`、width`y`height和 ` shapez`。同样,当shape字段为圆形时,TypeScript 也只允许您访问 ` radiusx` 和circle`y`,否则会抛出错误。
使用 in 运算符缩小范围
该in运算符用于确定对象是否具有名为 的属性。它的使用格式为 ,"property" in object其中property是要检查是否存在于 中的属性的名称object。
在上面的例子中,我们使用判别式并集来区分圆形和矩形。我们也可以使用in运算符来实现同样的效果,但这次我们将检查形状是否包含某些属性,例如,radius对于`Rectangle` Circle, ` Rectangle` width,以及对于 `Rectangle`,结果将相同。heightRectangle
type Circle = {
radius: number;
};
type Reactangle = {
width: number;
height: number;
};
function calculateArea(shape: Circle | Reactangle) {
if ("radius" in shape) {
// now you can access radius from shape
console.log("Area of circle: " + 3.14 * shape.radius * shape.radius);
// any attempt to access height or width will result to an error
shape.width; // Property 'width' does not exist on type 'Circle'.
shape.height; // Error: Property 'height' does not exist on type 'Circle'
}
if ("width" in shape && "height" in shape) {
// now you can access height and width from the shape object
console.log("Area of reactangle: " + shape.height * shape.width);
// any attempt to access raidus would result to an error
shape.radius; // Error: Property 'radius' does not exist on type 'Reactangle'.ts
}
}
使用赋值缩小
在这种类型缩减中,TypeScript 会在变量被赋值后缩减其类型。例如,假设变量 x 的类型为 `int`number或`int` string,如果我们赋值为 `int` number,则 x 的类型变为 `int` number;如果我们赋值为 `int` string,则 x 的类型变为 `string`。
let x : number | string = 1;
console.log(typeof x) // "number"
x = "something"
console.log(typeof x) // "string"
以下是 Code Sandbox 上的一个详细示例:
使用 instanceof 进行缩小范围
JavaScript 的 `is`instanceof运算符用于检查一个值是否是某个特定类的实例。它的使用格式为 `is`,value instanceof value2并返回一个布尔值。当您检查一个值是否为instanceof某个类时,TypeScript 会将该类型赋值给变量,从而缩小类型范围。
以下示例展示了一个函数接收一个日期参数,该日期可以是字符串或 Date 类型。如果是 Date 类型,我们希望将其转换为字符串;如果是字符串,则直接返回。我们可以使用 `instanceof` 来检查它是否是 Date 类型的实例,并将其转换为字符串,如下所示。
function dateToString(value: string | Date) {
if(value instanceof Date) {
// The type now is Date and you can access Date methods
return value.toISOString();
}
return value;
}
结论
在本文中,我们学习了多种缩小类型范围的方法,从类型守卫到可区分联合。在下一篇文章中,我们将学习如何使用类型谓词构建我们自己的类型守卫。
如果您觉得这篇文章对您有所帮助,并想继续学习,请访问我的全新 TypeScript 系列——“TypeScript 入门指南”(A Byte of TypeScript)。“TypeScript 入门指南”是我将定期更新的系列文章,旨在帮助您揭开 TypeScript 的神秘面纱。
文章来源:https://dev.to/this-is-learning/type-narrowing-in-typescript-3bdp