类型注解与类型推断
TypeScript 作为 JavaScript 的超集,通过静态类型检查显著提升了代码的可维护性和开发效率。类型注解与类型推断是 TypeScript 类型系统的核心机制,二者协同工作,既提供了灵活性,又减少了冗余代码。
类型注解的基本语法
类型注解是开发者显式声明变量、函数参数或返回值类型的方式。语法为 : Type
,直接附加在标识符后。例如:
let username: string = "Alice";
const age: number = 25;
function greet(name: string): void {
console.log(`Hello, ${name}`);
}
基础类型包括:
string
: 文本数据number
: 双精度浮点数boolean
: true/falsenull
/undefined
: 需配合严格空检查symbol
: 唯一标识符bigint
: 大整数
复杂类型注解示例:
// 数组
const scores: number[] = [90, 85, 95];
// 元组
let person: [string, number] = ["Bob", 30];
// 对象类型
const user: { id: number; name?: string } = { id: 1 };
类型推断的工作机制
当未显式注解类型时,TypeScript 编译器会根据上下文自动推导类型。例如:
let count = 10; // 推断为 number
const message = "Hello"; // 推断为 "Hello" (字面量类型)
const items = [1, "text", true]; // 推断为 (number | string | boolean)[]
函数返回值的典型推断:
function add(a: number, b: number) {
return a + b; // 返回值推断为 number
}
const result = add(3, 5); // result 推断为 number
注解与推断的协作模式
最佳实践是:
- 对函数参数进行显式注解
- 允许返回值类型推断
- 变量在声明时初始化则依赖推断
示例对比:
// 冗余注解
const price: number = 100;
// 更简洁的推断
const price = 100;
// 必要的参数注解
function calculateTotal(quantity: number, unitPrice: number) {
return quantity * unitPrice; // 返回值自动推断
}
需要强制注解的场景
某些情况下必须使用类型注解:
- 未初始化的变量:
let timer; // 隐式 any
let timer: number; // 正确做法
- 函数复杂返回值:
interface ApiResponse<T> {
data: T;
status: number;
}
function fetchData(): ApiResponse<string[]> {
return { data: ["item1", "item2"], status: 200 };
}
- 重载函数:
function parseInput(input: string): number;
function parseInput(input: number): string;
function parseInput(input: any): any {
/* 实现 */
}
高级类型推断技术
- 上下文推断(Contextual Typing):
const buttons = document.querySelectorAll(".btn"); // NodeListOf<HTMLButtonElement>
buttons.forEach(button => {
button.disabled = true; // 自动识别 button 类型
});
- 条件类型推断:
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type Test = UnpackArray<string[]>; // string
- 泛型约束推断:
function merge<T extends object, U extends object>(a: T, b: U) {
return { ...a, ...b }; // 返回 T & U
}
const merged = merge({ name: "Alice" }, { age: 30 }); // { name: string, age: number }
常见误区与解决方案
- 过度使用 any:
// 错误示范
function logValue(value: any) {
console.log(value.toUpperCase());
}
// 改进方案
function logValue(value: unknown) {
if (typeof value === "string") {
console.log(value.toUpperCase());
}
}
- 忽略严格空检查:
// tsconfig.json 需设置 strictNullChecks: true
function getLength(text?: string) {
return text.length; // 编译错误
// 正确写法
return text?.length ?? 0;
}
- 错误理解联合类型推断:
const values = [0, 1, "2"]; // (number | string)[]
values.push(true); // 错误:boolean 不能赋值给 number | string
工具链集成实践
- JSDoc 类型提示:
// @ts-check
/**
* @param {number} x
* @param {number} y
* @returns {number}
*/
function multiply(x, y) {
return x * y;
}
- 编辑器实时反馈:
interface Product {
id: number;
name: string;
price: number;
}
// 输入时自动提示 Product 的属性和方法
const product: Product = {
// 编辑器会提示需要实现的字段
};
- 类型断言的特殊场景:
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
// 等同于
const canvas = <HTMLCanvasElement>document.getElementById("canvas");
性能与最佳实践
- 避免深度嵌套推断:
// 复杂推断可能影响编译速度
type DeepNested<T> = {
data: T;
next?: DeepNested<T>;
};
// 适当拆分类型定义
interface Node<T> {
data: T;
next?: Node<T>;
}
- 合理使用类型别名:
// 过度使用
type ID = number;
type UserID = ID;
type ProductID = ID;
// 更语义化的定义
type UserID = number & { readonly __tag: unique symbol };
type ProductID = number & { readonly __tag: unique symbol };
- 编译时类型检查示例:
function processArray(arr: readonly number[]) {
arr.push(1); // 编译错误:readonly 限制
}
const nums = [1, 2, 3] as const; // 变为只读元组
nums[0] = 5; // 错误:无法修改
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:静态类型系统概述