类型断言
在 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 在缺少显式类型注释时,通过根据表达式的上下文(在这里是事件处理函数)来推断变量类型的一种方式。