TypeScript 基础
TypeScript 是 JavaScript 的超集:在 JS 之上增加了静态类型系统,编译时就能发现大量错误,配合编辑器实现智能提示与重构。它是如今大型前端项目的标配。
一、TypeScript 是什么
| 维度 | 说明 |
|---|---|
| 定位 | JavaScript 的超集,所有合法 JS 都是合法 TS |
| 核心能力 | 静态类型检查(编译期),运行时无额外开销 |
| 编译 | 通过 tsc 把 .ts 编译为 .js,类型注解会被剥离 |
| 与 JS 关系 | 类型是"可选的注释",不影响运行逻辑 |
text
TypeScript 源码(.ts)
→ tsc 编译
→ 剥离类型注解 + 转译新语法
→ 纯 JavaScript 输出(.js)
写代码时:类型约束 + 编辑器提示
运行时:类型消失,代码就是 JStypescript
// 编译前(TS):带类型注解
function add(a: number, b: number): number {
return a + b;
}javascript
// 编译后(JS):类型全部剥离
function add(a, b) {
return a + b;
}二、安装与配置
bash
npm install -D typescript
npx tsc --init # 生成 tsconfig.json
npx tsc # 编译
npx tsc --noEmit # 只做类型检查,不输出文件2.1 tsconfig.json 关键选项
json
{
"compilerOptions": {
"target": "ES2020", // 编译输出的目标语法版本
"module": "ESNext", // 模块规范(CommonJS / ESNext / NodeNext)
"strict": true, // 开启全部严格检查(强烈推荐)
"outDir": "./dist", // 编译产物输出目录
"rootDir": "./src", // 源码根目录
"moduleResolution": "bundler", // 模块解析策略
"esModuleInterop": true, // 兼容 CommonJS 默认导入
"skipLibCheck": true // 跳过 .d.ts 类型检查,加快编译
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}| 选项 | 作用 |
|---|---|
target | 输出 JS 的语法版本(ES5/ES2015/ES2020 等) |
module | 输出的模块规范 |
strict | 开启 strictNullChecks 等全部严格选项 |
outDir / rootDir | 输出目录与源码根目录 |
noImplicitAny | 禁止隐式 any(strict 已包含) |
strictNullChecks | 严格区分 null/undefined(TS 最重要的开关) |
三、基础类型
3.1 常用类型
typescript
const name: string = "张三";
const age: number = 18;
const isAdmin: boolean = true;
const list: number[] = [1, 2, 3];
const tuple: [string, number] = ["age", 18]; // 元组:固定长度与类型
const obj: { id: number; title: string } = { id: 1, title: "标题" };3.2 enum 枚举
typescript
enum Direction {
Up, // 0(默认从 0 递增)
Down = 5, // 可指定起始值
Left, // 6
Right = "RIGHT", // 字符串枚举
}
console.log(Direction.Up); // 0
console.log(Direction[5]); // "Down"(数字枚举可反向映射)3.3 any / unknown / void / never
typescript
let a: any = 1; // 任意类型,放弃类型检查(尽量少用)
let u: unknown = 1; // 未知类型,使用前必须收窄
function log(): void { // 无返回值
console.log("hi");
}
function fail(msg: string): never { // 永不返回(抛错或死循环)
throw new Error(msg);
}| 类型 | 含义 | 何时用 |
|---|---|---|
any | 任意类型,不做检查 | 迁移遗留代码的临时手段,尽量不用 |
unknown | 未知类型,需收窄后才能用 | 外部输入(JSON 解析、接口响应) |
void | 无返回值 | 不返回值的函数 |
never | 永不返回 | 抛异常、死循环、穷尽性检查 |
3.4 类型断言
类型断言告诉编译器"我比你更了解这个值的类型"(不会做运行时转换):
typescript
const el = document.getElementById("app") as HTMLDivElement;
const num = someUnknown as number; // as 语法
const num2 = <number>someUnknown; // 尖括号语法(JSX 中不可用)推荐先尝试收窄(typeof/instanceof),断言只在无法收窄时使用。
四、接口 interface
接口描述对象的形状:
typescript
interface User {
id: number;
name: string;
age?: number; // 可选属性
readonly email: string; // 只读属性:赋值后不可改
greet(): string; // 函数类型
[key: string]: unknown; // 索引签名:允许额外任意属性
}
const user: User = {
id: 1,
name: "李四",
email: "li@example.com",
greet: () => `你好,${user.name}`,
};typescript
// 函数类型接口
interface SumFn {
(a: number, b: number): number;
}
const sum: SumFn = (a, b) => a + b;4.1 接口继承与合并
typescript
interface Animal { name: string; }
interface Dog extends Animal { bark(): void; } // 继承
// 同名接口自动合并(声明合并)
interface Window { myGlobal: number; }
// 声明合并常用于扩展第三方库全局类型五、type 与 interface 对比
typescript
// type 别名:可以表示联合、交叉、元组、原始类型
type ID = string | number;
type Point = { x: number; y: number };
type Pair = [number, number];
type Handler = (e: Event) => void;
// 交叉类型
type Named = { name: string } & { age: number }; // 兼具两者| 维度 | interface | type |
|---|---|---|
| 适用对象 | 对象形状、函数签名 | 任意类型(联合/交叉/元组/原始类型) |
| 继承 | extends | & 交叉 |
| 声明合并 | 支持 | 不支持 |
| 对象类型扩展性 | 好 | 好 |
| 官方建议 | 对象优先用 interface,其余用 type | — |
typescript
// 常见实践:接口定义 props,type 定义联合
interface ButtonProps { text: string; }
type ButtonType = "primary" | "default" | "danger";六、泛型
泛型让"类型"成为参数,实现一个组件多种类型复用。
6.1 泛型函数与接口、类
typescript
// 泛型函数
function identity<T>(value: T): T {
return value;
}
const s = identity("hello"); // 自动推断 T = string
const n = identity<number>(1); // 显式指定
// 泛型接口
interface Box<T> { value: T; }
const box: Box<number> = { value: 1 };
// 泛型类
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
const numStack = new Stack<number>();6.2 泛型约束与默认值
typescript
// 约束:T 必须有 length 属性
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
getLength("abc"); // 3
getLength([1, 2, 3]); // 3
// 默认类型参数
function createArray<T = string>(length: number): T[] {
return new Array(length);
}七、类型守卫
类型守卫在运行时收窄联合类型的范围,让 TS 在分支内得到精确类型:
typescript
// typeof:收窄原始类型
function format(value: string | number): string {
if (typeof value === "string") return value.toUpperCase();
return value.toFixed(2); // 此处 TS 知道 value 是 number
}
// instanceof:收窄类实例
function play(x: Dog | Cat) {
if (x instanceof Dog) x.bark(); // 只有 Dog 有 bark
}
// in:收窄对象属性
function move(x: Bird | Fish) {
if ("fly" in x) x.fly(); // 有 fly 属性 → Bird
else x.swim(); // Fish
}typescript
// 自定义类型谓词:is 关键字
function isString(value: unknown): value is string {
return typeof value === "string";
}
function process(v: unknown) {
if (isString(v)) {
v.toUpperCase(); // 收窄为 string
}
}判别联合是日常最常用的模式——靠一个可区分字段收窄:
typescript
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
}
}八、联合与交叉类型
typescript
// 联合类型:或(Union)
type Status = "success" | "error" | "pending";
let s: Status = "success"; // 只能取这三个值之一
// 交叉类型:与(Intersection)
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge; // 同时具有 name 和 age| 类型 | 语义 | 示例 |
|---|---|---|
| 联合 `A | B` | 是 A 或 B 之一 |
交叉 A & B | 同时是 A 和 B | {name} & {age} |
九、编译流程
text
写 .ts 源码
→ tsc 类型检查(报告错误,不影响继续输出)
→ 剥离类型 + 转译新语法(target)
→ 输出 .js + .d.ts(声明文件)bash
npx tsc --noEmit # 只检查不输出(CI 常用)
npx tsc -w # 监听模式,改动即重新编译注意:TS 是"转译"不是"polyfill",新 API(如 Array.prototype.flat)仍需目标环境支持或引入 polyfill。
十、与 JavaScript 互操作
10.1 声明文件 .d.ts
JS 没有类型,给 JS 库补类型的方式就是写声明文件:
typescript
// lodash.d.ts(简化示例)
declare module "my-lib" {
export function format(input: string): string;
}社区已把主流库的声明打包进 DefinitelyTyped 仓库,一条命令即可安装:
bash
npm install -D @types/node # Node.js 内置模块类型
npm install -D @types/lodash # lodash 类型
npm install -D @types/express # Express 类型10.2 类型来源优先级
| 场景 | 类型来源 |
|---|---|
| 库自带类型 | 包内 types 字段指向的 .d.ts |
| 社区类型 | @types/xxx 包 |
| 无类型 | 自己写 declare module 或使用 any 过渡 |
10.3 在 JS 项目逐步引入
json
// tsconfig.json 中的宽松迁移配置
{
"compilerOptions": {
"allowJs": true, // 允许混用 .js 文件
"checkJs": false, // 暂不检查 JS 文件
"strict": false // 迁移期间先关闭严格模式
}
}典型工作流:JS 文件逐步改名为 .ts → 类型从宽松到严格 → 覆盖关键模块后再开启 strict。TypeScript 的设计目标之一就是"渐进式采用"。
TypeScript 的收益是长期的:编译期拦截错误、编辑器智能提示、重构安全。类型系统是给团队协作与长期维护买的一份保险。