ts类型断言与类型推断

类型断言

在 TypeScript 中,类型断言(Type Assertion)是一种告诉编译器"我知道这个值的类型是什么"的机制。这种机制主要有两种形式:尖括号语法(Angle Bracket Syntax)和 as 语法。

尖括号语法

typescript 复制代码
let value: any = "This is a string";
let strLength: number = (<string>value).length;

// 或者
let alternativeWay: number = (value as string).length;

在上面的例子中,value 被声明为 any 类型,然后通过尖括号语法或 as 语法将其断言为 string 类型,以便获取其 length 属性。这样,开发者告诉 TypeScript 编译器:"我确信 value 是一个字符串。"

as语法

ini 复制代码
let value: any = "This is a string";
let strLength: number = (value as string).length;

在这个例子中,我们使用了 as 语法来进行类型断言,效果和尖括号语法是一样的。


举个例子,现在有这样一个函数,

kotlin 复制代码
const getLength = target => {
  if (target.length) {
    return target.length;
  } else {
    return target.toString().length;
  }
};

这是一个 JavaScript 函数,用于获取目标对象的长度。函数采用一个参数 target,然后通过检查目标对象的 .length 属性是否存在来确定其类型。

如果目标对象有 .length 属性,函数直接返回该属性的值,假定该对象是一个具有长度属性的对象(比如数组、字符串等)。

如果目标对象没有 .length 属性,函数假设该对象可能是一个不具有 .length 属性的对象,然后通过将其转换为字符串并获取字符串的长度来返回对象的长度。

现在考虑这个函数的TypeScript实现,如果按下述方式编写

typescript 复制代码
const getLength = (target: string | number): number => {
  if (target.length) { // 类型"string | number"上不存在属性"length"。类型"number"上不存在属性"length"。
    return target.length; // error 报错信息看下方
  } else {
    return target.toString().length;
  }
};

函数的参数 target 被定义为 string | number,表示它可以是字符串或数字类型。然而,在 TypeScript 中,对于联合类型,你不能直接访问其类型的属性或方法,因为并非所有可能的类型都具有相同的属性。

在这个函数中,TypeScript 报错是因为它无法确定 target 到底是一个字符串还是一个数字,而它尝试访问 .length 属性,这个属性在数字类型上是不存在的。

因此可以考虑采用类型断言

typescript 复制代码
const getStrLength = (target: string | number): number => {
  if ((<string>target).length) { // 这种形式在JSX代码中不可以使用,而且也是TSLint不建议的写法
    return (target as string).length; // 这种形式是没有任何问题的写法,所以建议大家始终使用这种形式
  } else {
    return target.toString().length;
  }
};

我们希望对 target 进行判断并访问其 length 属性,但由于 target 是一个联合类型 string | number,TypeScript 不确定它的具体类型。因此,我们在进行判断之前需要告诉 TypeScript 我们认为 target 是一个字符串类型。

在这个函数中,使用了类型断言来告诉 TypeScript,你确定 target 是一个字符串。有两种类型断言的写法:

(<string>target).length:这是一种旧式的写法,不推荐在 JSX 代码中使用,而且 TSLint 也不建议这种写法。

(target as string).length:这是一种更现代、更安全的写法,也是推荐使用的方式。

所以,建议使用第二种写法,即 (target as string).length。这样可以避免潜在的问题,并且在 JSX 代码中也是可用的。

类型推断

多类型联合

ini 复制代码
let arr = [1, "a"];
arr = ["b", 2, false]; // error 不能将类型"false"分配给类型"string | number"

当我们定义一个数组或元组时,TypeScript 会对数组中元素的类型进行合并形成联合类型。这就意味着,数组或元组的元素可以是多个不同类型中的任意一种。

在给定的例子中,arr 的元素类型被推断为 string | number,这表示数组中的元素可以是 string 类型,也可以是 number 类型,但不允许其他类型,例如 boolean。所以,当尝试将 false 赋值给 arr 时,TypeScript 报错,因为 false 的类型是 boolean,不属于联合类型 string | number。

ini 复制代码
let value = Math.random() * 10 > 5 ? 'abc' : 123
value = false // error 不能将类型"false"分配给类型"string | number"

value 的类型被推断为联合类型 string | number。这是因为它的赋值取决于三元操作符的条件。如果条件为真,value 是字符串类型 'abc',如果条件为假,value 是数值类型 123。

当你尝试将 false 赋值给 value 时,TypeScript 报错。这是因为 false 的类型不属于 string | number 联合类型,所以 TypeScript 认为这是一个类型不匹配的错误。

这种方式使得 TypeScript 可以在编译阶段捕获到潜在的类型错误,增加了代码的类型安全性。

上下文类型

在 TypeScript 中,上下文类型是一种类型推断的机制,它是根据变量声明的上下文来推断该变量的类型。上下文类型通常发生在赋值语句的左侧,即当你给一个变量赋值时,TypeScript 根据变量被赋值的上下文来推断变量的类型。

javascript 复制代码
window.onmousedown = function(mouseEvent) {
  console.log(mouseEvent.a); // error 类型"MouseEvent"上不存在属性"a"
};

window.onmousedown 被赋值为一个函数,而该函数有一个参数 mouseEvent。TypeScript 尝试根据赋值语句的上下文来推断 mouseEvent 的类型。

上下文: window.onmousedown 是一个事件处理函数,因此 TypeScript 推断 mouseEvent 应该是鼠标事件对象 (MouseEvent)。

推断的类型: TypeScript 推断 mouseEvent 的类型为 MouseEvent,因为它是 onmousedown 事件处理函数的参数。

类型检查: 在你尝试访问 mouseEvent.a 时,TypeScript 进行了类型检查。由于 MouseEvent 类型并没有名为 a 的属性,所以 TypeScript 报错,指出在 MouseEvent 上不存在属性 a。

总体而言,上下文类型推断是 TypeScript 在缺少显式类型注释时,通过根据表达式的上下文(在这里是事件处理函数)来推断变量类型的一种方式。

相关推荐
j喬乔1 小时前
Node导入不了命名函数?记一次Bug的探索
typescript·node.js
yg_小小程序员12 小时前
vue3中使用vuedraggable实现拖拽
typescript·vue
高山我梦口香糖14 小时前
[react 3种方法] 获取ant组件ref用ts如何定义?
typescript·react
prall16 小时前
实战小技巧:下划线转驼峰篇
前端·typescript
一條狗2 天前
隨筆 20241224 ts寫入excel表
开发语言·前端·typescript
轻口味3 天前
配置TypeScript:tsconfig.json详解
ubuntu·typescript·json
小林rr4 天前
前端TypeScript学习day03-TS高级类型
前端·学习·typescript
web150850966414 天前
前端TypeScript学习day01-TS介绍与TS部分常用类型
前端·学习·typescript
前端熊猫5 天前
省略内容在句子中间
前端·javascript·typescript
禁止摆烂_才浅5 天前
React全家桶 -【高阶函数/高阶组件/钩子】-【forwardRef、mome、useImperativeHandle、useLayoutEffect】
react.js·typescript