1. TypeScript 概述
TypeScript 是由微软开发的、基于 JavaScript 的一种强类型编程语言。它是在 JavaScript 的基础上添加了静态类型检查、面向对象编程等功能的超集,最终会被编译为纯 JavaScript 代码。由于其扩展了 JavaScript 的功能,TypeScript 特别适合大型项目和团队协作,能够提高代码的可读性、可维护性和开发效率。
TypeScript 是由微软开发的、基于 JavaScript 的一种强类型编程语言。它是在 JavaScript 的基础上添加了静态类型检查、面向对象编程等功能的超集,最终会被编译为纯 JavaScript 代码。由于其扩展了 JavaScript 的功能,TypeScript 特别适合大型项目和团队协作,能够提高代码的可读性、可维护性和开发效率。
1.1 TypeScript 优势
提高代码质量:通过静态类型检查,TypeScript 能在编译阶段捕捉潜在的错误,从而减少运行时错误,提高代码的稳定性。更好的协作:类型信息使得团队中的其他开发者能够更容易理解代码结构和预期行为,改善协作效率。增强的开发工具:由于 TypeScript 是强类型语言,编辑器可以提供更好的代码补全、重构支持,以及更加详细的错误提示。兼容 JavaScript:TypeScript 是 JavaScript 的超集,意味着所有的 JavaScript 代码都是合法的 TypeScript 代码。可以逐步将现有的 JavaScript 项目迁移到 TypeScript。面向大型项目:TypeScript 特别适合开发大型代码库,因为它提供了更严格的类型检查和结构化的开发方式,确保代码更易于维护。
1.2 TypeScript 和 JavaScript 区别
TypeScript 和 JavaScript 在类型系统、编译过程、特性支持以及开发体验等方面存在显著差异。TypeScript 通过引入静态类型系统和一系列新特性,为 JavaScript 开发提供了更好的支持和保障。然而 JavaScript 作为一种广泛应用的脚本语言,其灵活性和动态性也是其独特优势之一。因此,在选择使用 TypeScript 还是 JavaScript 时,需要根据具体的项目需求和团队技术栈来做出决策。
类型系统区别
JavaScript:JavaScript是一种动态类型语言,其变量的类型是在运行时确定的,并且可以随时更改。这意味着在JavaScript中,你不需要(也不能)在编写代码时显式地声明变量的类型。
TypeScript:TypeScript引入了静态类型系统,允许开发者在编写代码时显式地声明变量、函数参数和返回值的类型。这使得TypeScript能够在编译时检查代码中的类型错误,提供更早的错误检测和更好的代码健壮性。
编译过程区别
JavaScript:JavaScript 代码可以直接在浏览器或者 Node.js 环境中运行,无需编译。它是一种解释型语言,代码在执行过程中被逐行解释。
TypeScript:TypeScript 代码需要通过编译器(如TypeScript Compiler,简称 tsc)将其编译成 JavaScript 代码,然后才能在浏览器或 Node.js 环境中运行。这个编译过程包括了类型检查、代码转换等步骤,确保了代码的质量。
特性支持区别
JavaScript:虽然 JavaScript 拥有广泛的应用和强大的生态系统,但其本身在类型检查、模块化、面向对象编程等方面存在局限性。不过,随着 ECMAScript 标准的不断发展,JavaScript 也在逐渐引入新的特性和语法来弥补这些不足。
TypeScript:TypeScript 在 JavaScript 的基础上添加了类型注解、接口、类、泛型等特性,使得代码更加结构化、可组织性更强,并且更易于维护和重构。此外 TypeScript 还支持 ECMAScript 的各个版本,并添加了一些额外的语言特性和功能。
开发体验区别
JavaScript:由于 JavaScript 的动态类型和解释型特性,开发者在编写代码时可能会遇到一些难以追踪的错误。此外,缺乏类型检查也使得代码的可读性和可维护性受到一定影响。
TypeScript:TypeScript 的静态类型系统、类型注解和类型推断等功能能够显著提高代码的可读性和可维护性。同时,TypeScript 还提供了强大的开发工具支持(如 Visual Studio Code 等),这些工具能够提供代码补全、类型检查、重构等功能,从而进一步提升开发效率。
特性 | JavaScript | TypeScript |
---|---|---|
类型系统 | 动态类型 | 静态类型,可选 |
编译 | 不需要编译,直接运行 | 需要编译为 JavaScript 运行 |
支持面向对象编程 | 有限支持 | 完整支持类、接口、抽象类等 |
开发工具支持 | 基础代码补全 | 强大的代码补全、类型推断、错误提示 |
泛型 | 不支持 | 支持 |
1.3 TypeScript 适用场景
大型项目:在大型项目中,代码的复杂度增加,TypeScript 的类型系统和模块化特性能够极大提升代码的可维护性和可扩展性。团队合作:TypeScript 能够在多人协作时提供统一的类型约束,减少沟通成本,避免类型不匹配导致的问题。需要高代码质量和安全性:如果项目要求严格的代码质量和运行时安全,TypeScript 提供的类型检查和编译时错误提示非常有用。
1.4 TypeScript 官方网站
官网提供了详尽的文档,包括TypeScript的入门指南、手册、教程等,帮助用户从基础到高级逐步掌握TypeScript。文档中包含了TypeScript的语法、类型系统、接口、泛型、枚举等核心概念的解释和示例。
中文官网:https://www.tslang.cn/
英文官网:https://www.typescriptlang.org/
2. TypeScript 特性
TypeScript 通过引入静态类型检查、提供更好的开发工具支持和增强代码可维护性等特性,提升了 JavaScript 的开发体验,使得代码更健壮、可靠,并且更易于协作和扩展。它适用于大型项目和团队开发,为 JavaScript 开发者提供了更强大的工具和能力。
2.1 静态类型(Static Typing)
TypeScript 允许开发者为变量、函数参数和返回值等指定类型,并在编译时进行类型检查。这有助于捕捉潜在的错误,并在编写代码时提供更好的代码提示和自动补全。
let age: number = 30;
let name: string = "John";
let isActive: boolean = true;function greet(userName: string): string {return `Hello, ${userName}`;
}
2.2 类型推断(Type Inference)
虽然 TypeScript 需要为变量指定类型,但如果没有明确指定类型,TypeScript 会根据上下文自动推断类型。这使得 TypeScript 在保证类型安全的同时,依然保持了编写 JavaScript 的简洁性。
let count = 10; // TypeScript 自动推断 count 的类型为 number
2.2 接口(Interfaces):
接口是 TypeScript 中的核心概念之一,它用于定义对象的结构。接口规定了对象必须包含的属性和方法。接口可以在类、函数参数、对象定义中使用,以确保代码的一致性和类型安全。
interface Person {name: string;age: number;greet(): void;
}let user: Person = {name: "Alice",age: 25,greet() {console.log("Hello!");}
};
2.3 类和继承(Classes and Inheritance)
TypeScript 完全支持面向对象编程,包括类、继承、接口实现、访问控制修饰符(例如: public
、private
和 protected
)。TypeScript 的类与 ES6 类语法非常类似,但它提供了更多功能,如抽象类和成员可见性控制。
class Animal {constructor(public name: string) {}move(distance: number) {console.log(`${this.name} moved ${distance} meters.`);}
}class Dog extends Animal {bark() {console.log("Woof! Woof!");}
}const dog = new Dog("Buddy");
dog.bark();
dog.move(10);
2.4 泛型(Generics)
TypeScript 支持泛型(Generics),使得函数、类和接口可以更灵活地处理不同的数据类型,同时保持类型安全。这对于创建可重用的组件非常有用,特别是在处理集合或映射类型时。
function identity<T>(arg: T): T {return arg;
}let output = identity<string>("Hello");
let output2 = identity<number>(123);
2.5 枚举(Enums)
枚举允许为一组相关的值定义有意义的名字,提供了更具可读性的代码。
enum Color {Red,Green,Blue}let c: Color = Color.Green;
2.6 类型别名(Type Aliases)
类型别名可以为类型定义一个简洁的名称,尤其适用于联合类型(Union Types)、交叉类型(Intersection Types)等复杂类型的场景。
type StringOrNumber = string | number;let value: StringOrNumber = "Hello";value = 123; // 也可以是数字
2.7 模块系统(Modules)
TypeScript 使用 ES6 的模块系统,可以使用 import
和 export
来组织代码。模块使得代码更加模块化、易于管理和复用。
utils.ts
export function add(x: number, y: number): number {return x + y;
}
main.ts
import { add } from './utils';
console.log(add(2, 3));
2.8 工具支持和开发体验
TypeScript 通过静态类型和类型检查,提供了强大的代码自动补全、重构和错误检测功能。大多数现代 IDE(如 Visual Studio Code、WebStorm)都提供了对 TypeScript 的良好支持,能够极大提升开发体验。
TypeScript 是一种在 JavaScript 之上扩展的语言,通过引入静态类型检查、面向对象特性和丰富的工具支持,极大地提升了代码的可维护性、可读性和开发效率。无论是大型项目还是小型应用,TypeScript 都能为开发者提供更好的编程体验。
3. 编译 TypeScript
要编译 TypeScript 代码,需要使用 TypeScript 编译器(tsc
),这是 TypeScript 官方提供的编译工具。编译 TypeScript 非常简单,主要步骤包括安装 TypeScript 编译器、编写 .ts
文件、使用 tsc
命令编译,并运行编译生成的 JavaScript 文件。通过 tsconfig.json
,可以定制编译选项和简化编译流程。下面是编译 TypeScript 代码的详细步骤。
3.1 安装 TypeScript 编译器
首先,你需要在本地环境中安装 TypeScript 编译器。可以通过 npm(Node.js 的包管理工具)来安装 TypeScript。此命令会全局安装 TypeScript 编译器,安装完成后,可以在命令行中使用 tsc
命令。
npm install -g typescript
3.2 编写 TypeScript 代码
创建一个扩展名为 .ts
的文件,例如 example.ts
,并编写一些 TypeScript 代码。
example.ts
function greet(name: string): string {return `Hello, ${name}!`;
}const user = "World";
console.log(greet(user));
3.3 编译 TypeScript 文件
在终端或命令行中,使用 tsc
命令编译 TypeScript 文件。运行此命令后,TypeScript 编译器会将 example.ts
编译为 JavaScript 文件,生成一个名为 example.js
的文件。
tsc example.ts
example.js(编译后 JavaScript)
function greet(name) {return "Hello, " + name + "!";
}
var user = "World";
console.log(greet(user));
3.4 执行编译后 JavaScript
编译完成后,生成的 JavaScript 文件可以直接在任何支持 JavaScript 的环境中运行,例如 Node.js 或浏览器。用 Node.js 来运行编译后的 example.js
文件。
node example.js
输出结果
Hello, World!
3.5 使用 tsconfig.json
文件(可选)
在实际项目中,可以通过创建一个 tsconfig.json
文件来配置 TypeScript 编译选项,从而简化编译过程。首先运行以下命令来生成 tsconfig.json
文件。
tsc --init
生成的 tsconfig.json
文件会包含多种编译选项,可以根据项目需求修改。
{"compilerOptions": {"target": "es6", // 指定 ECMAScript 目标版本"module": "commonjs", // 指定模块系统"strict": true, // 启用所有严格类型检查选项"outDir": "./dist", // 输出文件夹"rootDir": "./src", // 输入文件夹"esModuleInterop": true // 支持 ES 模块的互操作性},"include": ["src/**/*"], // 要编译的文件"exclude": ["node_modules"] // 排除的文件
}
当前工作目录有 tsconfig.json
文件后,你只需在当前工作目录运行 tsc
命令即可编译整个项目,不需要再进行逐个指定文件操作。
tsc
3.6 监视文件自动编译(可选)
可以使用 --watch
选项,让 TypeScript 在文件更改时自动重新编译。
tsc --watch
3.7 TypeScript 编译选项
TypeScript 编译器提供了很多选项,你可以在 tsconfig.json
中配置,也可以在命令行中使用。
--target:指定 ECMAScript 目标版本(如 ES5、ES6)。
--module:指定模块系统(如 `commonjs`、`esnext`)。
--outDir:指定编译输出的文件夹。
--strict:启用严格模式,包括所有类型检查规则。
4. 基本数据类型语法
TypeScript 的类型声明用于为变量、函数、类等指定数据类型,使得代码更加严谨和可读。类型声明是 TypeScript 区别于 JavaScript 的重要特性之一,它帮助开发者在编译时发现潜在的类型错误。
4.1 常用基本类型
number: 用于数字类型(包括整数和浮点数)。
string: 用于字符串类型。
boolean: 用于布尔类型,true 或 false。
array: 用于数组类型,可以指定数组中元素的类型。
tuple: 元组类型,可以指定不同索引上的元素类型。
enum: 枚举类型,用于定义一组命名常量。
any: 任意类型,用于绕过类型检查。
void: 用于函数没有返回值时。
null 和 undefined: 分别表示空值和未定义值。
never: 表示永远不会发生的类型(如抛出异常或无限循环的函数)。
基本类型
let age: number = 30;
let username: string = "Alice";
let isActive: boolean = true;
数组类型
let numbers: number[] = [1, 2, 3, 4];
let names: string[] = ["Alice", "Bob", "Charlie"];
元组类型
let person: [string, number] = ["Alice", 30];
枚举类型
enum Direction {Up = 1,Down,Left,Right
}
let dir: Direction = Direction.Up;
any 类型
let anything: any = 42;
anything = "hello"; // 可以赋值为任何类型
void 类型
function logMessage(message: string): void {console.log(message);
}
never 类型
function throwError(errorMsg: string): never {throw new Error(errorMsg);
}
TypeScript 类型推断(Type Inference)是指在不显式声明类型的情况下,TypeScript 编译器自动推断出变量、函数等的类型。类型推断能够减少代码中的显式类型声明,使代码更加简洁,同时仍然保持强类型检查的优势。
4.2 any
数据类型语法
any
是 TypeScript 中的一个特殊数据类型,它可以表示任意类型的数据。使用 any
类型时,相当于关闭了 TypeScript 的类型检查机制,允许该变量持有任意类型的值。虽然 any
提供了极大的灵活性,但会牺牲掉 TypeScript 的类型安全优势,因此建议在必要时使用。
声明变量 any
类型
let value: any;value = 123; // 可以是 number
value = "hello"; // 可以是 string
value = true; // 可以是 boolean
value = {}; // 可以是 object
value = [1, 2, 3]; // 可以是 array
在这个例子中,value
变量可以存储任何类型的数据,因为它被显式地声明为 any
类型。
跳过类型检查
使用 any
类型时,TypeScript 不会对该变量进行类型检查。这使得可以绕过一些严格的类型规则。
let value: any = 5;value = "hello"; // 类型检查器不会报错
value.foo(); // 即使调用不存在的方法,也不会报错
上面的代码不会引发 TypeScript 的类型检查错误,即使 value
是一个字符串而不是对象,也不会对 foo()
方法进行类型检查。
函数参数和返回值类型: any
你可以将函数的参数或返回值声明为 any
类型,允许传入或返回任意类型的数据。
function processData(data: any): any {return data;
}let result = processData("hello"); // 返回值可以是任意类型
result = processData(123);
any 和 其他类型混合使用
在需要灵活性的情况下,可以将 any
类型与其他类型混合使用。例如,当你处理外部库的输入、解析 JSON 数据或者在逐步迁移到 TypeScript 的代码中,你可能需要使用 any
。
let mixed: any[] = [1, "hello", true]; // 数组可以包含任意类型的元素
any
类型安全问题
尽管 any
可以提供极大的灵活性,但过度使用它可能会导致类型错误难以捕捉。下面是一个例子,展示了如何使用 any
会导致潜在的错误。
let data: any = "hello";
console.log(data.toFixed(2));
问题:编译时不会报错,但运行时会抛出错误,因为
data
实际上是一个字符串,而不是数字。
any
和 unknown
区别
any
是最宽松的类型,不进行任何检查。unknown
是一种更安全的任意类型,使用 unknown
时必须进行类型检查,确保使用的值类型是正确的。
let valueAny: any = "hello";
let valueUnknown: unknown = "hello";valueAny.trim(); // 不会报错
valueUnknown.trim(); // 报错,必须先进行类型检查if (typeof valueUnknown === "string") {valueUnknown.trim(); // 类型检查后才允许操作
}
any
允许你绕过 TypeScript 的类型检查系统,但滥用它会导致类型安全性降低。为了在代码中保持类型安全,应该尽量少用 any
,或者在必要的场景下使用。
any 使用场景
逐步迁移 JavaScript 代码到 TypeScript
:当你将 JavaScript 代码迁移到 TypeScript,但还没有足够时间为所有内容添加类型时,可以临时使用 any
。
处理动态内容
:例如解析 JSON 数据或使用第三方库时,输入可能是不确定的类型,这时可以使用 any
来表示动态类型。
4.3 unknown 数据类型语法
unknown
是 TypeScript 中用于表示未知类型的类型。它比 any
更安全,因为在使用 unknown
类型的值之前,必须先进行类型检查。这让 unknown
成为一种更严格的 “任意类型”。
使用 unknown
时,你不能随意对其进行操作,必须先确定它的具体类型,否则编译器会报错。这有助于在处理不确定类型时增加类型安全性。
声明 unknown
类型
let value: unknown;value = 123; // 可以是 number
value = "hello"; // 可以是 string
value = true; // 可以是 boolean
value = {}; // 可以是 object
value = [1, 2, 3]; // 可以是 array
unknown
类似于 any
,它可以保存任何类型的值,但你不能对其进行操作,除非先进行类型检查。
unknown 类型检查
在对 unknown
类型的值进行任何操作之前,必须先进行类型检查。TypeScript 强制要求在调用方法或进行其他操作前明确类型。
let value: unknown = "hello";value.trim(); // 错误: 类型“unknown”上不存在属性“trim”// 进行类型检查
if (typeof value === "string") {console.log(value.trim()); // 正确:value 现在被认为是 string
}if (typeof value === "number") {console.log(value.toFixed(2)); // 正确:value 现在被认为是 number
}
函数参数和返回值:unknown
你可以将函数的参数或返回值声明为 unknown
,这样调用者必须先检查类型,才能对返回的值进行操作。
function processValue(value: unknown) {if (typeof value === "string") {return value.trim();} else if (typeof value === "number") {return value.toFixed(2);}return null;
}let result = processValue(" hello "); // 正确:返回 "hello"
let result2 = processValue(123); // 正确:返回 "123.00"
unknown 类型断言
如果你确定某个 unknown
类型的值是某种类型,你可以使用类型断言来告诉 TypeScript 编译器这个值的类型。
let value: unknown = "hello";
console.log((value as string).trim()); // 使用类型断言,告诉编译器这是一个 string
类型断言不会做运行时检查,但它允许你在编译时绕过类型检查。使用时需要谨慎,确保类型断言是正确的。
unkown 与 never
的区别
unknown:表示一个未知的类型,使用前需要进行类型检查。
never:表示不可能的类型,例如永远不会返回的函数。
function throwError(message: string): never {throw new Error(message); // 这个函数永远不会正常结束
} let value: unknown = "hello";
if (typeof value === "string") {console.log(value); // value 现在是 string
}
unknown 总结
unknown
是处理未知类型时的一种推荐选择,它在提供灵活性的同时,确保了类型安全。unknown
是一个比 any
更安全的类型,当你需要处理任意类型的值但又希望保留类型安全时,使用 unknown
是一种更好的选择。在操作 unknown
类型的值之前,必须进行类型检查,否则会引发编译错误。unknown
强制开发者在使用值时明确其类型,减少了潜在的类型错误。
4.4 never 数据类型语法
never
是 TypeScript 中的一个特殊类型,表示那些永远不会有返回值的类型。never
类型不同于 void
,因为 void
表示函数可以正常返回 undefined
,而 never
表示函数永远不会成功地返回。
never 数据类型 使用场景
1. 无法到达的代码:例如总是抛出错误的函数,或包含无限循环的函数。
2. 类型保护中的穷尽性检查:当你在 `switch` 或 `if` 语句中处理了所有可能的类型,`never` 可以帮助确保没有遗漏。
never
抛出异常函数
当函数抛出异常且永远不会正常返回时,返回类型为 never
。在例子中,throwError
函数会抛出错误,而不会返回任何值,因此其返回类型是 never
。
function throwError(message: string): never {throw new Error(message);
}
never
无限循环函数
如果一个函数包含无限循环,它永远不会返回,因此返回类型也是 never
。在这个例子中,infiniteLoop
函数将永远执行,不会返回任何值,因此其返回类型是 never
。
function infiniteLoop(): never {while (true) {// 无限循环,永远不会返回}
}
never
穷尽性检查
never
类型在类型保护和穷尽性检查中非常有用。假设我们有一个类型联合,并在 switch
语句中处理每个类型。never
可以确保所有的类型都被正确处理。
type Animal = 'cat' | 'dog';function checkAnimal(animal: Animal) {switch (animal) {case 'cat':console.log("It's a cat");break;case 'dog':console.log("It's a dog");break;default:// 如果没有处理完所有可能的情况,TS 编译器会报错const _exhaustiveCheck: never = animal;throw new Error(`Unhandled case: ${animal}`);}
}
在这个例子中,default
分支中的 animal
被赋值为 never
类型,因为理论上它应该已经处理了 Animal
类型的所有可能值。如果新增一种 Animal
类型但没有处理,TypeScript 会报错,提醒开发者更新代码。
never
区别 void
和 never
void:表示函数可以返回,但返回值是 `undefined`,也就是说它是 "没有返回值" 的一种形式。
never:表示函数永远不会返回,即要么抛出异常,要么进入无限循环。
function returnVoid(): void {console.log("This function returns undefined implicitly.");
}function returnNever(): never {throw new Error("This function never returns.");
}
运行结果
returnVoid 函数可以正常执行并返回 `undefined`。
returnNever 函数永远不会执行到返回点。
never 数据类型总结
never
表示一个函数永远不会返回值,或者某些代码路径永远不会到达。它常用于抛出错误、无限循环以及类型保护中的穷尽性检查。never
提供了额外的类型安全,确保代码处理了所有可能的情况。
4.5 enum(枚举)数据类型语法
在 TypeScript 中,enum(枚举)是一种特殊的数据类型,用于定义一组命名的常量。通过使用枚举,可以为一组相关的值创建人类可读的名字,而不仅仅是使用简单的数字或字符串。枚举使代码更加可读和易于维护。
enum 枚举类型
数字枚举(Numeric Enums)
字符串枚举(String Enums)
异构枚举(Heterogeneous Enums)
常量枚举(Constant Enums)
enum 数字枚举:定义
数字枚举是最常见的枚举形式,枚举成员的值是自动递增的数字。在这个例子中,Direction.Up
的值默认为 0
,接下来的 Down
、Left
和 Right
依次递增,分别为 1
、2
和 3
。如果你不提供初始值,TypeScript 会自动从 0
开始递增。
enum Direction {Up,Down,Left,Right
}let dir: Direction = Direction.Up;
console.log(dir); // 输出 0
enum 数字枚举:手动赋值
你可以为枚举成员手动指定初始值,后续的成员依次递增。在这个例子中,Up
被显式设置为 1
,而 Down
、Left
和 Right
的值依次递增为 2
、3
和 4
。
enum Direction {Up = 1,Down,Left,Right
}console.log(Direction.Up); // 输出 1
console.log(Direction.Down); // 输出 2
enum 数字枚举:随机值
你也可以为每个成员手动指定值,而不依赖递增规则。
enum Direction {Up = 1,Down = 5,Left = 10,Right = 15
}console.log(Direction.Left); // 输出 10
enum 字符串枚举
字符串枚举允许将每个成员定义为一个字符串字面量。这对于需要有明确文本表示的情况非常有用。在字符串枚举中,每个枚举成员都需要手动赋值为字符串,并且不再自动递增。
enum Direction {Up = "UP",Down = "DOWN",Left = "LEFT",Right = "RIGHT"
}let dir: Direction = Direction.Left;
console.log(dir); // 输出 "LEFT"
enum 异构枚举
枚举也可以同时包含数字和字符串成员,这称为异构枚举。虽然可以定义异构枚举,但这种用法一般较少见,除非有特别的需求。
enum BooleanLikeHeterogeneousEnum {No = 0,Yes = "YES"
}console.log(BooleanLikeHeterogeneousEnum.No); // 输出 0
console.log(BooleanLikeHeterogeneousEnum.Yes); // 输出 "YES"
enum 反向映射
对于数字枚举,TypeScript 会为枚举成员生成双向映射(反向映射)。这意味着可以通过枚举的名称获取对应的值,也可以通过枚举的值获取对应的名称。这里,Direction[0]
返回 "Up"
,这是因为 TypeScript 自动创建了从数字到枚举名称的反向映射。
enum Direction {Up,Down,Left,Right
}console.log(Direction.Up); // 输出 0
console.log(Direction[0]); // 输出 "Up"
注意:字符串枚举没有反向映射。
enum 常量枚举
TypeScript 还提供了 const enum
,用于定义常量枚举。常量枚举在编译时被内联,从而不会生成额外的 JavaScript 代码。常量枚举在编译后直接内联,生成的代码没有枚举对象,从而提高性能并减少代码量。
const enum Direction {Up,Down,Left,Right
}let dir: Direction = Direction.Up;
console.log(dir); // 输出 0
enum 枚举类型
你可以将枚举作为类型使用,表示变量或参数只能是枚举中的值。
enum Direction {Up,Down,Left,Right
}function move(direction: Direction) {console.log("Moving", Direction[direction]);
}move(Direction.Up); // 输出: "Moving Up"
move(5); // 错误,5 不是 Direction 中的一个值
enum 枚举数据类型总结
enum
是 TypeScript 中用于定义一组命名常量的工具,可以是数字或字符串。数字枚举可以自动递增,字符串枚举需要显式赋值。枚举成员可以通过双向映射访问(数字枚举有反向映射,字符串枚举没有)。const enum
可以用于性能优化,在编译时直接内联枚举值。
4.6 元组(tuple)数据结构语法
在 TypeScript 中,元组(Tuple) 是一种特殊的数组类型,用于定义一组已知数量和类型的元素。与普通的数组不同,元组中的每个元素都可以有不同的类型,并且元素的数量是固定的。
元组(Tuple)定义
通过在方括号中指定类型列表来定义元组。
let myTuple: [string, number];
myTuple = ["hello", 42]; // 正确
myTuple = [42, "hello"]; // 错误:类型顺序不匹配
在这个例子中,myTuple
是一个包含两个元素的元组,第一个元素是 string
类型,第二个元素是 number
类型。
访问、操作 tuple 元组
普通数组一样访问和操作元组的元素。
let myTuple: [string, number];
myTuple = ["hello", 42];console.log(myTuple[0]); // 输出: "hello"
console.log(myTuple[1]); // 输出: 42
元组中的元素有各自的类型,可以使用这些类型进行操作。
myTuple[0] = "world"; // 正确
myTuple[1] = 99; // 正确
tuple 可选元素
元组的某些元素可以是可选的,通过在类型后面加上 ?
表示可选元素。在这个例子中,元组的第二个元素是可选的,所以你可以只提供第一个元素。
let myTuple: [string, number?];
myTuple = ["hello"]; // 正确
myTuple = ["hello", 42]; // 正确
tuple 使用展开运算符
你可以在元组类型中使用展开运算符(...
),以支持不定数量的某种类型的元素。举例这里,元组的第一个元素是字符串,接下来的元素可以是任意数量的数字。
let myTuple: [string, ...number[]];
myTuple = ["hello"]; // 正确
myTuple = ["hello", 1, 2, 3]; // 正确
tuple 元组类型使用场景
元组通常用于表示一个固定结构的数据,比如函数的返回值。
function useTuple(): [string, number] {return ["result", 200];
}const result = useTuple();
console.log(result[0]); // 输出: "result"
console.log(result[1]); // 输出: 200
tuple 元组类型限制
元组的长度是固定的,超出或少于指定长度都会导致编译错误。元组中的每个元素都有明确的类型和顺序。
let myTuple: [string, number];
myTuple = ["hello", 42]; // 正确
myTuple = ["hello"]; // 错误:缺少第二个元素
myTuple = ["hello", 42, 99]; // 错误:元素过多
tuple 元组类型总结
元组是一种已知长度和类型的数组,每个位置上的元素类型可以不同。元组常用于表示固定结构的数据,比如多类型的函数返回值。元组的长度和类型是严格受限的,不能随意更改。
5. 数据类型推断
TypeScript 类型推断(Type Inference)是指在不显式声明类型的情况下,TypeScript 编译器自动推断出变量、函数等的类型。类型推断能够减少代码中的显式类型声明,使代码更加简洁,同时仍然保持强类型检查的优势。
TypeScript 的类型推断机制可以自动为变量、函数、对象等推断出适当的类型,大大减少了显式类型声明的需求,同时保留了类型检查的优势。在绝大多数情况下,TypeScript 都能够根据代码的上下文推断出最合适的类型,从而使代码简洁、灵活并且安全。
5.1 变量声明类型推断
当声明变量时,如果初始化时赋值了某个值,TypeScript 会根据这个值推断出变量的类型,而不需要显式指定类型。
let age = 25; // 推断为 number
let name = "Alice"; // 推断为 string
let isActive = true; // 推断为 booleanlet someVar; // 推断为 any (如果没有赋值,推断为 `any` 类型)
someVar = 10;
someVar = "hello";
5.2 函数返回值类型推断
TypeScript 会根据函数的返回值自动推断出函数的返回类型,而不需要显式地指定返回类型。
function add(a: number, b: number) {return a + b; // 推断为 number
}function greet() {return "Hello, World!"; // 推断为 string
}
在上面的例子中,add
函数返回 number
类型,greet
函数返回 string
类型,TypeScript 自动推断出返回值的类型,而不需要显式声明。
5.3 上下文类型推断
有时,类型是根据上下文来推断的。例如,事件处理函数、回调函数的参数类型可以由上下文自动推断出来。在这个例子中,TypeScript 根据 window.onmousedown
的上下文,推断出 event
是一个 MouseEvent
对象,自动为 event
提供了类型。
window.onmousedown = function(event) {console.log(event.button); // `event` 自动推断为 MouseEvent 类型
};
5.4 数组类型推断
当声明数组时,TypeScript 会根据数组中的元素类型推断出整个数组的类型。
let numbers = [1, 2, 3]; // 推断为 number[]
let names = ["Alice", "Bob", "Charlie"]; // 推断为 string[]
如果数组包含不同类型的元素,TypeScript 会推断为联合类型。
let mixed = [1, "Alice", true]; // 推断为 (number | string | boolean)[]
5.5 对象字面量类型推断
TypeScript 可以根据对象字面量自动推断出对象的类型。
let person = {name: "Alice",age: 30
}; // 推断为 { name: string; age: number }
5.6 最佳通用类型 Best Common Type
最佳通用类型(Best Common Type)当一个数组中有多个不同类型的元素,TypeScript 会尝试推断出一个能够兼容所有元素的“最佳通用类型”。在这个例子中,TypeScript 根据数组中的元素自动推断出数组的类型为 string | number | boolean
的联合类型,因为这是最能兼容所有元素的类型。
let array = [1, 2, "hello", true]; // 推断为 (string | number | boolean)[]
5.7 返回值类型推断
函数的参数和返回值类型可以基于上下文进行推断。例如,回调函数的参数类型可以根据函数调用的上下文进行推断。
let numbers = [1, 2, 3];numbers.forEach((num) => {console.log(num); // `num` 自动推断为 number
});
5.8 类型声明推断
在某些情况下,你可以同时使用类型推断和显式类型声明。例如,你可以为函数参数声明类型,而让 TypeScript 自动推断返回值类型。
function multiply(a: number, b: number) {return a * b; // TypeScript 自动推断返回值为 number
}
5.9 类型兼容性推断
在推断过程中,TypeScript 会根据赋值的上下文检查类型的兼容性。如果推断出的类型与上下文不兼容,TypeScript 会报错。
let num = 10; // 推断为 number
num = "hello"; // 报错:不能将类型 "string" 分配给类型 "number"
5.10 泛型类型推断
TypeScript 也可以在使用泛型时自动推断出类型。例如,当你调用一个泛型函数时,TypeScript 会根据传递的参数自动推断出泛型的类型。
function identity<T>(arg: T): T {return arg;
}let output1 = identity(10); // TypeScript 推断 T 为 number
let output2 = identity("hello"); // TypeScript 推断 T 为 string
6. TypeScript 面向对象(更新中)
更新中···