TypeScript 基础类型系统详解
目录
TypeScript 概述
什么是 TypeScript?
TypeScript 是 Microsoft 开发的开源编程语言,它是 JavaScript 的一个超集,添加了静态类型定义。TypeScript 代码会被编译成纯 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。
核心优势
- 静态类型检查:在编译时发现错误,而不是运行时
- 更好的 IDE 支持:自动完成、重构、导航等功能
- 代码可读性:类型注解使代码意图更清晰
- 团队协作:类型定义作为代码契约,减少沟通成本
- 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript
环境搭建
# 全局安装 TypeScript
npm install -g typescript
# 检查版本
tsc --version
# 初始化 TypeScript 项目
tsc --init
# 编译 TypeScript 文件
tsc filename.ts
# 监听模式编译
tsc --watch
原始类型详解
1. 字符串类型 (string)
// 基本字符串
let firstName: string = "张";
let lastName: string = '三';
// 模板字符串
let fullName: string = `${firstName}${lastName}`;
let greeting: string = `Hello, ${fullName}!`;
// 多行字符串
let multiLine: string = `
这是一个
多行字符串
示例
`;
// 字符串方法的类型推断
let message: string = "Hello World";
let upperMessage: string = message.toUpperCase(); // TypeScript 知道这返回 string
let messageLength: number = message.length; // TypeScript 知道这返回 number
2. 数字类型 (number)
// 十进制
let decimal: number = 6;
let float: number = 3.14;
let negative: number = -42;
// 十六进制
let hex: number = 0xf00d;
// 二进制 (ES2015)
let binary: number = 0b1010;
// 八进制 (ES2015)
let octal: number = 0o744;
// 大数字 (ES2020)
let bigInt: bigint = 100n;
// 特殊数值
let notANumber: number = NaN;
let infinity: number = Infinity;
let negativeInfinity: number = -Infinity;
// 数字方法的类型推断
let num: number = 123.456;
let rounded: number = num.toFixed(2); // 注意:toFixed 返回 string,这里会报错
let correctRounded: string = num.toFixed(2); // 正确的类型
3. 布尔类型 (boolean)
// 基本布尔值
let isDone: boolean = false;
let isActive: boolean = true;
// 布尔表达式
let isGreater: boolean = 10 > 5;
let isEqual: boolean = "hello" === "world";
// 逻辑运算
let result1: boolean = true && false;
let result2: boolean = true || false;
let result3: boolean = !true;
// 条件判断中的类型推断
function checkStatus(isActive: boolean): string {
if (isActive) {
return "激活状态";
} else {
return "非激活状态";
}
}
4. null 和 undefined
// 基本用法
let u: undefined = undefined;
let n: null = null;
// 在严格模式下,null 和 undefined 只能赋值给自己或 void
let strictUndefined: undefined = undefined;
let strictNull: null = null;
// 联合类型中的使用
let nullable: string | null = null;
let optional: string | undefined = undefined;
// 可选参数和属性
function greet(name?: string): string {
if (name === undefined) {
return "Hello, stranger!";
}
return `Hello, ${name}!`;
}
interface User {
name: string;
email?: string; // 等同于 email: string | undefined
}
5. void 类型
// 函数没有返回值
function logMessage(message: string): void {
console.log(message);
// 隐式返回 undefined
}
// 显式返回 undefined
function doNothing(): void {
return undefined;
}
// void 类型的变量只能赋值为 undefined 或 null(在非严格模式下)
let unusable: void = undefined;
6. never 类型
// 永远不会返回的函数
function error(message: string): never {
throw new Error(message);
}
// 无限循环
function infiniteLoop(): never {
while (true) {
// 无限循环
}
}
// never 类型在联合类型中会被忽略
type Example = string | number | never; // 等同于 string | number
// 用于详尽性检查
type Shape = "circle" | "square" | "triangle";
function getArea(shape: Shape): number {
switch (shape) {
case "circle":
return Math.PI * 1 * 1;
case "square":
return 1 * 1;
case "triangle":
return 0.5 * 1 * 1;
default:
// 如果 Shape 类型添加了新值但这里没有处理,TypeScript 会报错
const exhaustiveCheck: never = shape;
return exhaustiveCheck;
}
}
数组和元组类型
1. 数组类型的定义方式
// 方式一:类型[]
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["hello", "world"];
let booleans: boolean[] = [true, false, true];
// 方式二:Array<类型>
let numbersGeneric: Array<number> = [1, 2, 3, 4, 5];
let stringsGeneric: Array<string> = ["hello", "world"];
// 混合类型数组
let mixed: (string | number)[] = ["hello", 42, "world", 100];
let mixedGeneric: Array<string | number> = ["hello", 42, "world", 100];
2. 数组操作和类型推断
let fruits: string[] = ["apple", "banana", "orange"];
// 数组方法的类型推断
let firstFruit: string | undefined = fruits[0]; // 可能是 undefined
let fruitCount: number = fruits.length;
let upperFruits: string[] = fruits.map(fruit => fruit.toUpperCase());
let longFruits: string[] = fruits.filter(fruit => fruit.length > 5);
// 数组解构
let [first, second, ...rest] = fruits;
// first: string | undefined
// second: string | undefined
// rest: string[]
// 添加元素
fruits.push("grape"); // ✅ 正确
// fruits.push(123); // ❌ 错误:类型不匹配
3. 只读数组
// ReadonlyArray 类型
let readonlyNumbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
let readonlyStrings: readonly string[] = ["hello", "world"];
// 只读数组不能修改
// readonlyNumbers.push(6); // ❌ 错误:push 方法不存在
// readonlyNumbers[0] = 10; // ❌ 错误:不能赋值
// 但可以读取
let firstNumber: number = readonlyNumbers[0];
let numbersLength: number = readonlyNumbers.length;
// 可以通过类型断言转换为可变数组
let mutableNumbers: number[] = readonlyNumbers as number[];
4. 元组类型详解
// 基本元组
let person: [string, number] = ["张三", 30];
let coordinate: [number, number] = [10, 20];
// 访问元组元素
let name: string = person[0];
let age: number = person[1];
// let invalid = person[2]; // ❌ 错误:索引超出范围
// 元组解构
let [personName, personAge] = person;
// 可选元组元素
let optionalTuple: [string, number?] = ["hello"];
let optionalTuple2: [string, number?] = ["hello", 42];
// 剩余元素
let restTuple: [string, ...number[]] = ["hello", 1, 2, 3, 4];
let namedRestTuple: [name: string, ...scores: number[]] = ["张三", 90, 85, 92];
// 只读元组
let readonlyTuple: readonly [string, number] = ["hello", 42];
// readonlyTuple[0] = "world"; // ❌ 错误:只读
5. 复杂数组类型示例
// 对象数组
interface User {
id: number;
name: string;
email: string;
}
let users: User[] = [
{ id: 1, name: "张三", email: "zhangsan@example.com" },
{ id: 2, name: "李四", email: "lisi@example.com" }
];
// 函数数组
type MathOperation = (a: number, b: number) => number;
let operations: MathOperation[] = [
(a, b) => a + b,
(a, b) => a - b,
(a, b) => a * b,
(a, b) => a / b
];
// 嵌套数组
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 元组数组
let points: [number, number][] = [
[0, 0],
[10, 10],
[20, 30]
];
枚举类型深入
1. 数字枚举
// 基本数字枚举
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 使用枚举
let dir: Direction = Direction.Up;
console.log(dir); // 输出: 0
console.log(Direction[0]); // 输出: "Up" (反向映射)
// 自定义起始值
enum Status {
Pending = 1,
Approved, // 2
Rejected // 3
}
// 自定义所有值
enum HttpStatus {
OK = 200,
NotFound = 404,
InternalServerError = 500
}
2. 字符串枚举
// 字符串枚举
enum Color {
Red = "red",
Green = "green",
Blue = "blue"
}
// 使用字符串枚举
let favoriteColor: Color = Color.Red;
console.log(favoriteColor); // 输出: "red"
// 字符串枚举没有反向映射
// console.log(Color["red"]); // ❌ 错误
// 实际应用示例
enum LogLevel {
ERROR = "error",
WARN = "warn",
INFO = "info",
DEBUG = "debug"
}
function log(level: LogLevel, message: string): void {
console.log(`[${level.toUpperCase()}] ${message}`);
}
log(LogLevel.INFO, "应用启动成功");
3. 异构枚举(不推荐)
// 混合数字和字符串(不推荐使用)
enum Mixed {
No = 0,
Yes = "YES"
}
4. 常量枚举
// 常量枚举在编译时会被内联
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [
Directions.Up,
Directions.Down,
Directions.Left,
Directions.Right
];
// 编译后的 JavaScript:
// let directions = [0, 1, 2, 3];
5. 枚举的高级用法
// 计算成员
enum FileAccess {
None,
Read = 1 << 1, // 2
Write = 1 << 2, // 4
ReadWrite = Read | Write, // 6
}
// 枚举作为类型
function processStatus(status: HttpStatus): string {
switch (status) {
case HttpStatus.OK:
return "请求成功";
case HttpStatus.NotFound:
return "资源未找到";
case HttpStatus.InternalServerError:
return "服务器内部错误";
default:
return "未知状态";
}
}
// 枚举的 keyof 操作
type StatusKeys = keyof typeof HttpStatus; // "OK" | "NotFound" | "InternalServerError"
// 获取枚举的所有值
function getAllStatusValues(): HttpStatus[] {
return Object.values(HttpStatus);
}
对象类型
1. 对象类型的基本定义
// 内联对象类型
let user: { name: string; age: number; email: string } = {
name: "张三",
age: 30,
email: "zhangsan@example.com"
};
// 可选属性
let partialUser: { name: string; age?: number } = {
name: "李四"
// age 是可选的
};
// 只读属性
let readonlyUser: { readonly id: number; name: string } = {
id: 1,
name: "王五"
};
// readonlyUser.id = 2; // ❌ 错误:只读属性
2. 索引签名
// 字符串索引签名
interface StringDictionary {
[key: string]: string;
}
let dict: StringDictionary = {
"name": "张三",
"city": "北京",
"country": "中国"
};
// 数字索引签名
interface NumberArray {
[index: number]: string;
}
let arr: NumberArray = ["first", "second", "third"];
// 混合索引签名
interface MixedDictionary {
[key: string]: string | number;
[index: number]: string; // 数字索引的类型必须是字符串索引类型的子类型
length: number; // 具体属性
}
3. 嵌套对象类型
interface Address {
street: string;
city: string;
zipCode: string;
}
interface Person {
name: string;
age: number;
address: Address;
contacts: {
email: string;
phone?: string;
};
}
let person: Person = {
name: "张三",
age: 30,
address: {
street: "长安街1号",
city: "北京",
zipCode: "100000"
},
contacts: {
email: "zhangsan@example.com"
}
};
函数类型
1. 函数类型的定义
// 函数声明
function add(a: number, b: number): number {
return a + b;
}
// 函数表达式
let multiply = function(a: number, b: number): number {
return a * b;
};
// 箭头函数
let divide = (a: number, b: number): number => a / b;
// 完整的函数类型
let subtract: (a: number, b: number) => number = function(a, b) {
return a - b;
};
2. 可选参数和默认参数
// 可选参数
function greet(firstName: string, lastName?: string): string {
if (lastName) {
return `Hello, ${firstName} ${lastName}!`;
}
return `Hello, ${firstName}!`;
}
greet("张三"); // ✅ 正确
greet("张", "三"); // ✅ 正确
// 默认参数
function createUser(name: string, age: number = 18, role: string = "user"): object {
return { name, age, role };
}
createUser("张三"); // age=18, role="user"
createUser("李四", 25); // role="user"
createUser("王五", 30, "admin"); // 所有参数都指定
3. 剩余参数
// 剩余参数
function sum(first: number, ...rest: number[]): number {
return first + rest.reduce((a, b) => a + b, 0);
}
sum(1); // 1
sum(1, 2, 3, 4); // 10
// 剩余参数的类型推断
function logMessages(prefix: string, ...messages: string[]): void {
messages.forEach(message => {
console.log(`${prefix}: ${message}`);
});
}
logMessages("INFO", "应用启动", "数据库连接成功", "服务器就绪");
4. 函数重载
// 函数重载声明
function processData(data: string): string;
function processData(data: number): number;
function processData(data: boolean): boolean;
// 函数实现
function processData(data: string | number | boolean): string | number | boolean {
if (typeof data === "string") {
return data.toUpperCase();
} else if (typeof data === "number") {
return data * 2;
} else {
return !data;
}
}
// 使用
let result1 = processData("hello"); // string
let result2 = processData(42); // number
let result3 = processData(true); // boolean
5. 高阶函数类型
// 接受函数作为参数
function applyOperation(
a: number,
b: number,
operation: (x: number, y: number) => number
): number {
return operation(a, b);
}
// 使用
let result = applyOperation(10, 5, (x, y) => x + y); // 15
// 返回函数的函数
function createMultiplier(factor: number): (value: number) => number {
return function(value: number): number {
return value * factor;
};
}
let double = createMultiplier(2);
let triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
联合类型和交叉类型
1. 联合类型 (Union Types)
// 基本联合类型
let value: string | number;
value = "hello"; // ✅ 正确
value = 42; // ✅ 正确
// value = true; // ❌ 错误
// 函数参数的联合类型
function formatValue(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toString();
}
}
// 字面量联合类型
type Status = "loading" | "success" | "error";
let currentStatus: Status = "loading";
// 对象联合类型
interface Bird {
type: "bird";
flySpeed: number;
}
interface Fish {
type: "fish";
swimSpeed: number;
}
type Animal = Bird | Fish;
function moveAnimal(animal: Animal): void {
switch (animal.type) {
case "bird":
console.log(`Flying at ${animal.flySpeed} km/h`);
break;
case "fish":
console.log(`Swimming at ${animal.swimSpeed} km/h`);
break;
}
}
2. 交叉类型 (Intersection Types)
// 基本交叉类型
interface Person {
name: string;
age: number;
}
interface Employee {
employeeId: number;
department: string;
}
type PersonEmployee = Person & Employee;
let employee: PersonEmployee = {
name: "张三",
age: 30,
employeeId: 12345,
department: "IT"
};
// 函数类型的交叉
type Logger = {
log: (message: string) => void;
};
type Timestamped = {
timestamp: Date;
};
type TimestampedLogger = Logger & Timestamped;
let logger: TimestampedLogger = {
log: (message: string) => console.log(message),
timestamp: new Date()
};
3. 类型守卫 (Type Guards)
// typeof 类型守卫
function processValue(value: string | number): string {
if (typeof value === "string") {
// 在这个块中,TypeScript 知道 value 是 string
return value.toUpperCase();
} else {
// 在这个块中,TypeScript 知道 value 是 number
return value.toString();
}
}
// instanceof 类型守卫
class Dog {
bark(): void {
console.log("Woof!");
}
}
class Cat {
meow(): void {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat): void {
if (animal instanceof Dog) {
animal.bark(); // TypeScript 知道这是 Dog
} else {
animal.meow(); // TypeScript 知道这是 Cat
}
}
// 自定义类型守卫
function isString(value: unknown): value is string {
return typeof value === "string";
}
function processUnknown(value: unknown): void {
if (isString(value)) {
// TypeScript 知道 value 是 string
console.log(value.toUpperCase());
}
}
字面量类型
1. 字符串字面量类型
// 字符串字面量类型
type Direction = "north" | "south" | "east" | "west";
function move(direction: Direction): void {
console.log(`Moving ${direction}`);
}
move("north"); // ✅ 正确
// move("up"); // ❌ 错误
// 模板字面量类型 (TypeScript 4.1+)
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type HoverEvent = EventName<"hover">; // "onHover"
// 更复杂的模板字面量
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiEndpoint<T extends string, M extends HttpMethod> = `${M} /api/${T}`;
type UserEndpoints =
| ApiEndpoint<"users", "GET"> // "GET /api/users"
| ApiEndpoint<"users", "POST"> // "POST /api/users"
| ApiEndpoint<"users", "PUT"> // "PUT /api/users"
| ApiEndpoint<"users", "DELETE">; // "DELETE /api/users"
2. 数字字面量类型
// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
function rollDice(): DiceRoll {
return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}
// 配置对象中的字面量类型
interface Config {
version: 1 | 2 | 3;
mode: "development" | "production" | "test";
port: 3000 | 8080 | 9000;
}
let config: Config = {
version: 2,
mode: "development",
port: 3000
};
3. 布尔字面量类型
// 布尔字面量类型
type Success = true;
type Failure = false;
interface ApiResult<T extends boolean> {
success: T;
data: T extends true ? any : null;
error: T extends false ? string : null;
}
let successResult: ApiResult<true> = {
success: true,
data: { id: 1, name: "张三" },
error: null
};
let failureResult: ApiResult<false> = {
success: false,
data: null,
error: "网络错误"
};
类型断言
1. 基本类型断言
// 尖括号语法(在 JSX 中不可用)
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法(推荐)
let someValue2: unknown = "this is a string";
let strLength2: number = (someValue2 as string).length;
// DOM 元素的类型断言
let inputElement = document.getElementById("myInput") as HTMLInputElement;
inputElement.value = "Hello World";
2. 非空断言操作符
// 非空断言操作符 (!)
function processUser(user: { name: string; email?: string }): void {
// 我们确定 email 存在
let emailLength = user.email!.length;
console.log(`Email length: ${emailLength}`);
}
// 数组元素的非空断言
let numbers = [1, 2, 3, 4, 5];
let lastNumber = numbers.pop()!; // 我们确定数组不为空
3. 双重断言
// 双重断言(谨慎使用)
let value: string = "hello";
let numberValue: number = (value as unknown) as number;
// 这样做是危险的,应该避免
4. const 断言
// const 断言
let colors = ["red", "green", "blue"] as const;
// colors 的类型是 readonly ["red", "green", "blue"]
let config = {
apiUrl: "https://api.example.com",
timeout: 5000
} as const;
// config 的类型是 { readonly apiUrl: "https://api.example.com"; readonly timeout: 5000; }
// 用于创建字面量类型
function handleStatus(status: "success" | "error" | "loading"): void {
// 处理状态
}
let currentStatus = "success" as const;
handleStatus(currentStatus); // ✅ 正确,因为 currentStatus 的类型是 "success"
实践练习
练习1:用户管理系统
// 定义用户角色枚举
enum UserRole {
Admin = "admin",
User = "user",
Guest = "guest"
}
// 定义用户状态
type UserStatus = "active" | "inactive" | "pending";
// 定义用户接口
interface User {
readonly id: number;
username: string;
email: string;
role: UserRole;
status: UserStatus;
profile?: {
firstName: string;
lastName: string;
avatar?: string;
};
createdAt: Date;
lastLoginAt?: Date;
}
// 创建用户函数
function createUser(
username: string,
email: string,
role: UserRole = UserRole.User
): Omit<User, 'id' | 'createdAt'> {
return {
username,
email,
role,
status: "pending"
};
}
// 用户验证函数
function validateUser(user: User): boolean {
return user.username.length > 0 &&
user.email.includes('@') &&
Object.values(UserRole).includes(user.role);
}
练习2:购物车系统
// 商品接口
interface Product {
id: number;
name: string;
price: number;
category: string;
inStock: boolean;
}
// 购物车项目
interface CartItem {
product: Product;
quantity: number;
}
// 购物车类
class ShoppingCart {
private items: CartItem[] = [];
addItem(product: Product, quantity: number = 1): void {
if (!product.inStock) {
throw new Error("商品缺货");
}
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
}
removeItem(productId: number): void {
this.items = this.items.filter(item => item.product.id !== productId);
}
getTotal(): number {
return this.items.reduce((total, item) => {
return total + (item.product.price * item.quantity);
}, 0);
}
getItems(): readonly CartItem[] {
return this.items;
}
}
练习3:API 响应处理
// API 响应基础接口
interface BaseApiResponse {
status: number;
message: string;
timestamp: Date;
}
// 成功响应
interface SuccessResponse<T> extends BaseApiResponse {
success: true;
data: T;
}
// 错误响应
interface ErrorResponse extends BaseApiResponse {
success: false;
error: {
code: string;
details?: string;
};
}
// 联合类型
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
// API 调用函数
async function fetchUser(id: number): Promise<ApiResponse<User>> {
try {
// 模拟 API 调用
const user: User = {
id,
username: "testuser",
email: "test@example.com",
role: UserRole.User,
status: "active",
createdAt: new Date()
};
return {
success: true,
status: 200,
message: "用户获取成功",
timestamp: new Date(),
data: user
};
} catch (error) {
return {
success: false,
status: 500,
message: "服务器错误",
timestamp: new Date(),
error: {
code: "INTERNAL_ERROR",
details: error instanceof Error ? error.message : "未知错误"
}
};
}
}
// 响应处理函数
function handleUserResponse(response: ApiResponse<User>): void {
if (response.success) {
console.log("用户信息:", response.data);
} else {
console.error("错误:", response.error.code, response.error.details);
}
}
总结
TypeScript 的基础类型系统为我们提供了强大的类型安全保障。通过掌握这些基础概念:
- 原始类型:string, number, boolean, null, undefined, void, never
- 数组和元组:提供集合数据的类型安全
- 枚举:创建命名常量集合
- 对象类型:描述复杂数据结构
- 函数类型:确保函数签名的正确性
- 联合和交叉类型:组合类型以创建更复杂的类型
- 字面量类型:精确的值类型
- 类型断言:在必要时覆盖类型推断
这些基础知识将为学习更高级的 TypeScript 特性打下坚实的基础。在实际项目中,建议从简单的类型注解开始,逐步引入更复杂的类型定义,以提高代码的可维护性和可靠性。