类型断言
类型断言是一种 TypeScript 特性,用于告诉编译器将一个值视为特定的类型,即使编译器本身的类型推断可能不同。
类型断言并不会改变变量的实际运行时类型,而是在编译阶段告知TypeScript编译器开发者期望该表达式具有某种类型。
注意:类型断言 不是 类型转换,因为转换通常意味着某种运行时的支持。但类型断言纯粹是一个编译时语法,只是在编译阶段起作用,不进行特殊的数据检查和解构,它没有运行时的影响。同时,它也是一种为编译器提供关于如何分析代码的方法。
语法格式:
<类型>值
值 as 类型
类型断言主要用于以下情况:
- 当 TypeScript 的类型推断不够精确,而开发者明确知道变量的实际类型时。
- 当需要访问特定类型的属性或方法,但 TypeScript 由于自身的类型限制不允许直接访问时。
let someValue: any = "Hello, TypeScript!";
// 方式一:<类型>值
let length1: number = (<string>someValue).length;
// 方式二:值 as 类型
let length2: number = (someValue as string).length;
当你在TypeScript里使用JSX
时,只有 as
语法断言是被允许的。
类型推断
在 TypeScript 中,类型推论允许 TypeScript 在某些情况下根据变量的初始化值自动推断出变量的类型。
类型推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时。
- 变量初始化时的类型推论
当声明一个变量并同时为其赋值时,TypeScript 会根据赋值的值来推断变量的类型。
示例:
let num = 5; // TypeScript 会推断 num 的类型为 number
let isTrue = true; // TypeScript 会推断 isTrue 的类型为 boolean
let arr = [1, 2, 3]; // TypeScript 会推断 arr 的类型为 number[]
let str = "Hello"; // TypeScript 会推断 str 的类型为 string
str = "hi"; // 合法,str 的类型为 string
str = 10; // Error: 不能将类型“number”分配给类型“string”。
当需要从几个表达式中推断类型时候,会使用这些表达式的类型来推断出一个最合适的通用类型。
let arr = [0, 1, null];
数组 arr
中的元素包含了数字 0
和 1
,以及 null
,所以数组元素的类型被推断为 number
或 null
的联合类型,整个数组的类型就是 (number | null)[]
。
- 函数返回值的类型推论
如果函数的返回值是通过计算得出的,并且没有显式指定返回值类型,TypeScript 会根据返回的表达式来推断函数的返回值类型。
示例:
function sum(a: number, b: number) {
return a + b; // TypeScript 会推断该函数的返回值类型为 number
}
- 对象属性的类型推论
在对象初始化时,TypeScript 也会对对象属性进行类型推论。
示例:
// TypeScript 会推断 person 的类型为具有 name: string 和 age: number 属性的对象类型
let person = {
name: "John",
age: 30
};
// 对象字面量只能指定已知属性,并且“firstName”不在类型“{ name: string; age: number; }”中。
person = {
firstName: "John",
lastName: "Bob"
}
由于最终的通用类型取自候选类型,有些时候 候选类型 共享 相同的通用类型,但是却没有一个类型能做为所有候选类型的类型。
没有通用类型:
let zoo = [new Rhino(), new Elephant(), new Snake()];
在这种情况下,TypeScript 编译器 会根据数组中元素的实际类型来推断数组的类型。
在数组中, Rhino
、 Elephant
和 Snake
是自定义的类,它们之间没有找到最佳通用类型,类型推断的结果为联合数组类型:(Rhino | Elephant | Snake)[]
。
如果想要TypeScript编译器 推断zoo
的类型为Animal[]
,但是这个数组里没有对象是Animal
类型的,因此不能推断出这个结果。
当候选类型不能作为最佳通用类型使用的时候,需要明确的指出类型:
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
既然TypeScript 编译器会自动判断变量的类型,那为什么还要在编写代码时明确类型声明呢?
JavaScript的变量没有类型,在运行时明确变量的值的类型。
在单纯的为变量a
赋值时,a
是什么类型都影响不大:a = 10
、a = "hello"
、 a = true
。
假如a
作为某个函数的参数,需要传递到函数中使用呢?
function sum(a, b) {
return a + b;
}
console.log( sum(10, 15) ); // 25
console.log( sum("10", 15) ); // "1015"
假如要将计算结果进行展示或在别的函数里继续使用,会导致更多错误。
sum(10, 15)
和 sum("10", 15)
的计算结果相差很大,但是代码不会报错,那排查问题就会特别困难。
因此,明确声明该参数的类型可以防止意外传递错误类型的值,并且在阅读函数定义时就能清楚了解输入的预期。
function sum(a:number, b:number) {
return a + b;
}
console.log( sum(10, 15) ); // 25
console.log( sum("10", 15) ); // Error: 类型“string”的参数不能赋给类型“number”的参数。
明确指出变量类型有以下好处:
- 增强代码的自解释性
- 明确的类型声明使其他开发者在阅读代码时能更快速、清晰地理解变量的预期用途和可能的值。
- 提高代码的可维护性
- 当代码规模较大或经过多人协作开发时,显式的类型有助于确保代码的一致性和规范性。
- 未来进行代码修改和扩展时,清晰的类型信息可以降低出错的风险。
- 提前发现错误,增强类型安全性
- 显式声明类型,编译器能在编译阶段检测到更多潜在的类型不匹配错误,减少潜在的运行时错误。
- 更好的开发工具支持
- 开发工具可以基于明确的类型提供更准确和有用的代码提示、自动完成和重构功能。
- 文档化代码
- 类型声明相当于为代码添加了一种内置的文档,方便后续开发者理解。
类型断言、类型推论的区别
类型断言
- 手动干预:是开发者手动告诉编译器将某个值视为特定的类型,即使编译器本身的推断可能不同。
- 显式语法:通过特定的语法
<类型>值
或值 as 类型
来实现。 - 可能存在风险:如果断言不正确,可能在运行时导致错误,因为编译器在编译阶段不会深入检查断言的正确性。
类型推论(Type Inference):
- 自动进行:TypeScript 编译器根据变量的初始化值或函数的返回值等上下文信息,自动推断出变量或返回值的类型。
- 无需显式操作:开发者不需要进行任何特殊的语法或操作来触发类型推论,它是在编译时由编译器自动完成的。
- 基于值:类型的推断是基于所赋的值或计算得出的结果。