TypeScript 完全指南(大纲)
第1章:TypeScript 简介
1.1 什么是 TypeScript?
TypeScript 是微软开发的开源编程语言,它是 JavaScript 的超集 (Superset),意味着所有合法的 JavaScript 代码都可以直接在 TypeScript 中运行。TypeScript 的核心特性是静态类型检查 ------开发者可以在编码阶段为变量、函数参数、返回值等标注类型,编译器(tsc
)会在代码运行前检查类型错误,从而提前发现潜在问题。例如:
typescript
function add(a: number, b: number): number {
return a + b;
}
// 调用时传入非数字会触发编译错误:add("1", 2);
1.2 为什么选择 TypeScript?
- 类型安全 :减少运行时错误(如
undefined is not a function
)。 - 开发体验:IDE 智能提示、自动补全和重构支持(如 VS Code)。
- 渐进式:支持逐步迁移,可在现有 JavaScript 项目中局部引入。
- 生态兼容:编译后的代码是纯 JavaScript,兼容所有浏览器和 Node.js。
1.3 快速上手
-
安装环境:
bashnpm install -g typescript # 全局安装 TypeScript 编译器 tsc --version # 验证安装
-
编写第一个程序 :
创建
hello.ts
:typescriptlet message: string = "Hello, TypeScript!"; console.log(message);
-
编译与运行:
bashtsc hello.ts # 生成 hello.js node hello.js # 输出结果
1.4 TypeScript 的定位
TypeScript 并非替代 JavaScript,而是通过类型系统增强其工程化能力,尤其适合大型项目或团队协作。下一章将深入讲解基础类型与语法。
第2章:基础类型与语法
TypeScript 的基础类型系统通过显式注解增强了代码的可读性和安全性。合理使用这些类型可以大幅减少运行时错误,提升开发效率。
2.1 原始类型(Primitive Types)
TypeScript 的原始类型与 JavaScript 一致,但需要通过类型注解显式声明。
1. string
-
表示文本数据,可用单引号、双引号或反引号(模板字符串)。
-
严格类型检查:无法赋值非字符串值。
typescriptlet name: string = "Alice"; let greeting: string = `Hello, ${name}!`; // 模板字符串 // name = 100; // 错误:不能将 number 赋值给 string
2. number
-
包括整数、浮点数、二进制、八进制、十六进制等所有数值类型。
-
没有独立的
int
或float
,统一用number
。typescriptlet decimal: number = 6; let hex: number = 0xf00d; // 十六进制 let binary: number = 0b1010; // 二进制 let infinity: number = Infinity;
3. boolean
-
仅允许
true
或false
。 -
禁止隐式转换 :不能直接用
0
、""
等表示假值。typescriptlet isDone: boolean = false; // isDone = ""; // 错误:不能将 string 赋值给 boolean
2.2 数组与元组
1. 数组(Array)
-
语法 :
类型[]
或Array<类型>
。 -
所有元素必须为同一类型。
typescriptlet numbers: number[] = [1, 2, 3]; let names: Array<string> = ["Alice", "Bob"]; // numbers.push("4"); // 错误:类型不匹配
2. 元组(Tuple)
-
固定长度和类型的数组,适合存储结构化数据。
-
越界检查:超出定义长度会报错。
typescriptlet user: [string, number] = ["Alice", 30]; // user = ["Bob", 40, true]; // 错误:长度超过 2
-
可选元素与剩余元素(TypeScript 4.0+):
typescriptlet option: [string, number?] = ["Alice"]; // 第二个元素可选 let list: [string, ...number[]] = ["Alice", 1, 2]; // 剩余元素为 number 数组
2.3 枚举(Enum)
- 用于定义命名常量集合,支持数字或字符串值。
1. 数字枚举
-
默认从
0
开始自增,可手动赋值。typescriptenum Direction { Up = 1, // 显式赋值 Down, // 自动递增为 2 Left = 10, Right // 自动递增为 11 } console.log(Direction.Up); // 输出: 1
2. 字符串枚举
-
必须为每个成员显式赋值字符串。
typescriptenum LogLevel { Error = "ERROR", Warn = "WARN" }
3. 常量枚举(Const Enum)
-
编译时会被删除,直接替换为常量值,减少代码体积。
typescriptconst enum Size { Small = 10, Medium, Large } let size = Size.Medium; // 编译后:let size = 11;
2.4 any
、unknown
、void
、never
1. any
-
关闭类型检查:可赋值任意类型,慎用(破坏类型安全)。
typescriptlet data: any = "Hello"; data = 100; // 合法 data.toFixed(); // 编译通过,但运行时可能报错!
2. unknown
-
类型安全的
any
:必须通过类型断言或收窄后使用。typescriptlet input: unknown = fetchUserInput(); // let name: string = input; // 错误:unknown 不能直接赋值 if (typeof input === "string") { let name: string = input; // 类型收窄后合法 }
3. void
-
表示函数没有返回值。
typescriptfunction logMessage(msg: string): void { console.log(msg); // 无 return 语句 }
4. never
-
表示函数永远不会返回(如抛出错误或无限循环)。
typescriptfunction error(message: string): never { throw new Error(message); } function infiniteLoop(): never { while (true) {} }
第3章:类型进阶
TypeScript 的类型系统不仅限于基础类型,还提供了强大的工具来描述复杂的数据结构和逻辑。本章将深入讲解接口(Interface) 、联合类型(Union Types) 、类型别名(Type Alias) 、类型断言(Type Assertion) 和 类型守卫(Type Guard) 等高级类型用法。
3.1 接口(Interface)
接口用于定义对象的形状(属性和方法),是 TypeScript 的核心类型工具。
1. 基本用法
typescript
interface User {
name: string;
age: number;
}
const alice: User = {
name: "Alice",
age: 30,
// isAdmin: true // 错误:接口未定义 isAdmin
};
2. 可选属性与只读属性
- 可选属性 :通过
?
标记。 - 只读属性 :通过
readonly
标记,初始化后不可修改。
typescript
interface Config {
readonly id: string; // 只读
title: string;
maxRetries?: number; // 可选
}
const config: Config = {
id: "123",
title: "API Config",
};
// config.id = "456"; // 错误:只读属性不可修改
3. 函数类型
接口可以描述函数结构:
typescript
interface SearchFunc {
(source: string, keyword: string): boolean;
}
const search: SearchFunc = (src, kw) => src.includes(kw);
4. 接口继承
通过 extends
实现接口复用:
typescript
interface Animal {
name: string;
}
interface Dog extends Animal {
bark(): void;
}
const myDog: Dog = {
name: "Buddy",
bark: () => console.log("Woof!"),
};
5. 索引签名
描述动态属性名的类型,常用于字典结构:
typescript
interface StringArray {
[index: number]: string; // 索引为数字,值为字符串
}
const arr: StringArray = ["Alice", "Bob"];
console.log(arr[0]); // 输出: "Alice"
3.2 联合类型与类型别名
1. 联合类型(Union Types)
允许变量为多种类型之一:
typescript
type ID = string | number;
function printId(id: ID) {
console.log(id);
}
printId("ABC123"); // 合法
printId(100); // 合法
2. 类型别名(Type Alias)
为复杂类型定义简洁的名称:
typescript
type Point = {
x: number;
y: number;
};
type Callback = (data: string) => void;
3. 联合类型与字面量
结合字面量类型限制取值范围:
typescript
type Status = "success" | "error" | "pending";
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
let currentStatus: Status = "success";
let roll: Dice = 4;
3.3 类型断言
手动指定值的类型,适用于开发者比编译器更清楚类型的情况。
1. as
语法
typescript
const input = document.getElementById("input") as HTMLInputElement;
input.value = "Hello"; // 断言后可以安全访问 value 属性
2. 非空断言(!
)
明确告诉编译器值不为 null
/undefined
:
typescript
function getLength(str?: string) {
return str!.length; // 慎用:需确保 str 不为空
}
3.4 类型守卫
通过逻辑判断缩小变量类型范围。
1. typeof
守卫
typescript
function printValue(value: string | number) {
if (typeof value === "string") {
console.log(value.toUpperCase());
} else {
console.log(value.toFixed(2));
}
}
2. instanceof
守卫
typescript
class Cat { meow() {} }
class Dog { bark() {} }
function handlePet(pet: Cat | Dog) {
if (pet instanceof Cat) {
pet.meow();
} else {
pet.bark();
}
}
3. 自定义类型守卫
通过返回值为 value is Type
的函数定义逻辑:
typescript
interface Fish { swim(): void }
interface Bird { fly(): void }
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}
}
3.5 类型兼容性
TypeScript 使用结构化类型系统(Duck Typing),只要结构匹配即可兼容。
1. 对象兼容性
typescript
interface Named { name: string }
let x: Named;
const y = { name: "Alice", age: 30 };
x = y; // 合法:y 包含 name 属性
2. 函数兼容性
参数类型和返回值类型需兼容:
typescript
type Handler = (a: number) => void;
const handler1: Handler = (a: number) => {};
const handler2: Handler = (a: number, b: string) => {}; // 错误:参数数量不兼容
第4章:函数与类
TypeScript 在函数和类的定义中引入了严格的类型系统,使得面向对象编程更加安全和可维护。本章将详细讲解函数类型、类的基本语法、继承机制、访问修饰符等核心内容,并深入探讨抽象类与接口的应用。
4.1 函数类型
1. 函数参数与返回值的类型注解
-
参数类型:显式声明参数类型。
-
返回值类型:可显式标注返回值类型,编译器会自动推断。
typescriptfunction add(a: number, b: number): number { return a + b; } // 箭头函数类型 const multiply = (x: number, y: number): number => x * y;
2. 可选参数与默认参数
-
可选参数 :通过
?
标记,必须位于必选参数之后。 -
默认参数:直接赋值默认值,类型可自动推断。
typescriptfunction greet(name: string, greeting: string = "Hello", suffix?: string): string { return `${greeting}, ${name}${suffix ? " " + suffix : ""}!`; } greet("Alice"); // 输出: "Hello, Alice!" greet("Bob", "Hi", "Mr."); // 输出: "Hi, Bob Mr.!"
3. 剩余参数(Rest Parameters)
-
将多个参数收集为数组,类型必须为数组。
typescriptfunction sum(...numbers: number[]): number { return numbers.reduce((acc, curr) => acc + curr, 0); } sum(1, 2, 3); // 输出: 6
4. 函数重载(Function Overloads)
-
为同一函数提供多个类型定义,解决多态场景的类型问题。
typescript// 重载签名 function formatInput(input: string): string; function formatInput(input: number): string; // 实现签名(需兼容所有重载) function formatInput(input: string | number): string { return typeof input === "string" ? input.trim() : input.toFixed(2); } formatInput(" text "); // 返回 "text" formatInput(3.1415); // 返回 "3.14"
4.2 类的类型化
1. 类的基本语法
-
属性类型 :必须在类中显式声明类型(除非通过构造函数赋值且启用
strictPropertyInitialization
)。 -
构造函数 :使用
constructor
定义。typescriptclass Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } sayHello(): string { return `Hello, I'm ${this.name}`; } } const alice = new Person("Alice", 30);
2. 访问修饰符(Access Modifiers)
-
public
:默认修饰符,可在任何地方访问。 -
private
:仅类内部访问。 -
protected
:类内部和子类中访问。typescriptclass Animal { public name: string; private secret: string = "hidden"; protected age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } } class Dog extends Animal { bark() { console.log(this.age); // 合法:protected 属性
第5章:泛型(Generics)
泛型是 TypeScript 中用于编写可复用、灵活且类型安全的代码的核心工具。它允许开发者定义函数、类或接口时不预先指定具体类型,而是在使用时动态指定类型,从而避免重复代码并保持类型约束。本章将深入讲解泛型的使用场景、语法和高级技巧。
5.1 泛型的基本概念
1. 为什么需要泛型?
假设需要编写一个函数,返回传入的任意类型的参数:
typescript
// 无泛型:使用 any 会丢失类型信息
function identity(arg: any): any {
return arg;
}
const num = identity(100); // num 的类型为 any,无法推断为 number
使用泛型可以保留类型信息:
typescript
function identity<T>(arg: T): T {
return arg;
}
const num = identity<number>(100); // num 类型为 number
const str = identity("Hello"); // 类型自动推断为 string
2. 泛型语法
- 类型参数 :使用
<T>
定义泛型参数(T
是约定俗成的名称,可自定义)。 - 动态绑定:调用时显式指定类型或由编译器自动推断。
5.2 泛型函数
1. 基本用法
typescript
function logAndReturn<T>(value: T): T {
console.log(value);
return value;
}
logAndReturn<string>("Hello"); // 显式指定类型
logAndReturn(42); // 自动推断为 number
2. 多类型参数
可定义多个泛型参数,用逗号分隔:
typescript
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result = swap(["Alice", 30]); // 类型推断为 [number, string]
5.3 泛型接口
1. 接口中的泛型
定义可复用的通用结构:
typescript
interface KeyValuePair<K, V> {
key: K;
value: V;
}
const pair1: KeyValuePair<string, number> = { key: "age", value: 30 };
const pair2: KeyValuePair<number, boolean> = { key: 1, value: true };
2. 函数类型泛型
描述通用函数结构:
typescript
interface GenericFunc<T> {
(arg: T): T;
}
const numericFunc: GenericFunc<number> = (x) => x * 2;
numericFunc(10); // 返回 20
5.4 泛型类
1. 类中的泛型
用于创建可处理多种类型的容器类:
typescript
class Box<T> {
private content: T;
constructor(value: T) {
this.content = value;
}
getValue(): T {
return this.content;
}
}
const numberBox = new Box<number>(100);
const stringBox = new Box("Hello"); // 自动推断为 Box<string>
2. 泛型约束
限制泛型参数的类型范围,使用 extends
关键字:
typescript
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length);
}
logLength("Hello"); // 合法,string 有 length 属性
logLength([1, 2, 3]); // 合法,数组有 length
// logLength(100); // 错误:number 没有 length
3. keyof
约束
确保访问对象属性的安全性:
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "Alice", age: 30 };
getProperty(user, "name"); // 返回 "Alice"
// getProperty(user, "email"); // 错误:email 不是 user 的属性
5.5 泛型工具类型
TypeScript 内置了一些泛型工具类型,简化常见类型操作。
1. Partial<T>
将类型 T
的所有属性变为可选:
typescript
interface User {
name: string;
age: number;
}
type PartialUser = Partial<User>;
// 等效于 { name?: string; age?: number; }
2. Readonly<T>
将类型 T
的所有属性变为只读:
typescript
type ReadonlyUser = Readonly<User>;
// 等效于 { readonly name: string; readonly age: number; }
3. Pick<T, K>
从类型 T
中选择部分属性 K
:
typescript
type UserName = Pick<User, "name">; // { name: string }
4. Record<K, T>
定义键为 K
、值为 T
的对象类型:
typescript
type UserMap = Record<string, User>;
// 等效于 { [key: string]: User }
5.6 泛型默认类型
为泛型参数提供默认值,简化调用时的类型声明:
typescript
interface ResponseData<T = string> {
code: number;
data: T;
}
const stringResponse: ResponseData = { code: 200, data: "OK" }; // T 默认为 string
const numberResponse: ResponseData<number> = { code: 200, data: 100 };
5.7 泛型的实际应用
1. API 响应类型
定义通用的 API 响应结构:
typescript
interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
}
// 用户数据接口
interface UserData { id: string; name: string }
// 具体调用
const userResponse: ApiResponse<UserData> = await fetchUser();
2. React 组件 Props
在 React 中定义泛型组件:
typescript
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <div>{items.map(renderItem)}</div>;
}
// 使用示例
<List<number> items={[1, 2, 3]} renderItem={(n) => <div>{n}</div>} />;
第6章:模块与命名空间
模块(Module)和命名空间(Namespace)是 TypeScript 中管理代码作用域、组织复杂项目的核心机制。它们帮助开发者避免全局命名冲突,实现代码的模块化和复用。本章将深入探讨模块的导入导出、命名空间的设计模式,以及如何结合现代工具构建可维护的代码结构。
6.1 模块(Modules)
1. 模块的基本概念
- 模块化编程 :将代码拆分为独立的文件,每个文件是一个模块,通过
import
/export
管理依赖。 - 作用域隔离:模块内的变量、函数、类默认在模块作用域内,不污染全局。
2. ES Module 语法
-
导出(Export) :
typescript// math.ts export const PI = 3.14; // 导出常量 export function sum(a: number, b: number): number { // 导出函数 return a + b; } export class Calculator { // 导出类 static multiply(a: number, b: number): number { return a * b; } } // 默认导出(每个模块仅一个) export default class AdvancedCalculator extends Calculator {}
-
导入(Import) :
typescript// app.ts import { PI, sum } from "./math"; // 命名导入 import AdvancedCalculator from "./math"; // 默认导入 import * as MathUtils from "./math"; // 全部导入为命名空间 console.log(PI); // 3.14 console.log(sum(2, 3)); // 5 const calc = new AdvancedCalculator();
3. 模块编译与配置
- 模块类型 :在
tsconfig.json
中指定模块系统(如"module": "ES2020"
或"CommonJS"
)。 - 文件扩展名 :TypeScript 支持
.ts
、.tsx
、.mts
(ES 模块)和.cts
(CommonJS 模块)。
6.2 命名空间(Namespaces)
1. 命名空间的作用
- 逻辑分组:将相关功能组织在单一命名空间内,避免全局污染。
- 兼容性:适用于旧代码或浏览器环境(现代项目推荐使用模块)。
2. 定义与使用
typescript
// utilities.ts
namespace Utilities {
export function log(msg: string): void { // 导出才能在外部访问
console.log(msg);
}
export namespace MathHelper { // 嵌套命名空间
export const PI = 3.14;
}
}
// 使用命名空间
Utilities.log("Hello"); // 输出: Hello
console.log(Utilities.MathHelper.PI); // 输出: 3.14
3. 多文件命名空间
通过 /// <reference path="..." />
合并多个文件的命名空间:
-
file1.ts:
typescriptnamespace Utilities { export function log(msg: string) { ... } }
-
file2.ts:
typescript/// <reference path="file1.ts" /> namespace Utilities { export function debug(msg: string) { ... } }
-
编译后 :两个文件的
Utilities
命名空间会被合并。
6.3 模块与命名空间的对比
特性 | 模块 | 命名空间 |
---|---|---|
作用域 | 文件作用域 | 全局或命名空间作用域 |
依赖管理 | 显式 import /export |
隐式通过命名空间引用 |
适用场景 | 现代浏览器/Node.js 项目 | 旧项目或浏览器环境 |
编译输出 | 根据配置生成 ES/CommonJS 模块 | 合并为全局对象或 IIFE |
推荐使用 | ✅ 优先选择模块 | ⚠️ 仅在必要时使用 |
6.4 模块解析策略
1. 解析方式
- 相对路径 :
import "./math"
解析为当前目录下的文件。 - 非相对路径 :
import "lodash"
从node_modules
或配置的路径解析。
2. 路径别名配置
在 tsconfig.json
中配置 paths
简化导入路径:
json
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@utils/*": ["utils/*"] // 将 @utils/ 映射到 src/utils/
}
}
}
使用示例:
typescript
import { log } from "@utils/logger"; // 实际路径: src/utils/logger.ts
6.5 声明文件(.d.ts)
1. 声明文件的作用
为第三方 JavaScript 库或全局变量提供类型定义,增强 TypeScript 支持。
2. 声明全局模块
typescript
// global.d.ts
declare module "jquery" {
export function $(selector: string): any;
export namespace $ {
function ajax(url: string): void;
}
}
3. 声明全局变量
typescript
declare const VERSION: string; // 声明全局常量
declare interface Window {
MY_APP_CONFIG: Record<string, any>; // 扩展全局 Window 类型
}
6.6 模块的进阶用法
1. 动态导入(Dynamic Import)
按需加载模块,返回 Promise:
typescript
async function loadModule() {
const math = await import("./math");
console.log(math.sum(1, 2));
}
2. 重新导出(Re-export)
聚合多个模块的导出:
typescript
// utils/index.ts
export { log } from "./logger";
export { encrypt } from "./crypto";
3. 模块的副作用
执行模块代码但不导入任何内容:
typescript
import "./polyfills"; // 执行 polyfills.ts 中的初始化代码