D23. Typescript 的模块化
3.1. 🌟 为 JavaScript 编写类型声明文件
3.1.1. 声明文件的编写步骤
核心思想: 通过 .d.ts
文件为 JavaScript 模块添加类型信息,实现类型安全的引用。
步骤 1:创建声明文件
typescript
// my-js-module.d.ts
// 注意:无需实现代码,仅描述类型
步骤 2:定义模块导出
typescript
// 命名导出
export declare function add(a: number, b: number): number;
// 默认导出
export default declare class Calculator {
multiply(a: number, b: number): number;
}
步骤 3:关联模块 在 tsconfig.json
中包含声明文件:
json
{
"include": ["src", "my-js-module.d.ts"]
}
3.1.2. 全局变量 vs 模块声明
全局变量声明
typescript
// global.d.ts
declare const globalVar: string; // 声明全局变量
declare function globalFunction(): void; // 声明全局函数
模块化声明
typescript
// module-a.d.ts
export interface Config {
host: string;
}
export function start(config: Config): void;
TIP
- 全局变量声明无需
export
,直接挂载到全局作用域。 - 模块声明需通过
import
/require
引入。
3.1.3. 实战案例:给第三方库添加类型
场景:第三方库 math-utils.js
未提供类型声明,需手动补全。
javascript
// math-utils.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
export { add, multiply };
声明文件 math-utils.d.ts
typescript
// math-utils.d.ts
export declare function add(a: number, b: number): number;
export declare function multiply(a: number, b: number): number;
使用示例
typescript
import { add, multiply } from "./math-utils";
console.log(add(1, 2)); // 合法
// console.log(add("1", 2)); // ❌ 类型错误
3.2. 🌟 export 语句:导出成员与类型
3.2.1. 命名导出与默认导出
命名导出(Named Export)
typescript
// math.ts
export function add(a: number, b: number): number {
return a + b;
}
export interface Config {
precision: number;
}
默认导出(Default Export)
typescript
// calculator.ts
export default class Calculator {
multiply(a: number, b: number): number {
return a * b;
}
}
TIP
- 命名导出需通过
import { ... }
引入,名称需与导出名一致。 - 默认导出可任意命名,但需使用
import X from '...'
。
3.2.2. 导出类型与接口
导出接口
typescript
// types.ts
export interface User {
id: number;
name: string;
}
export type Status = "active" | "inactive";
导出类型别名
typescript
// utils.ts
export type Callback = (result: any) => void;
export function process(data: any, callback: Callback): void {
// ...
}
3.2.3. 导出合并与扩展
接口合并
typescript
// module-a.ts
export interface User {
id: number;
}
// module-b.ts
export interface User {
name: string;
}
// 合并后:User = { id: number, name: string }
类型扩展
typescript
// base.ts
export class BaseService {
start(): void {}
}
// extended.ts
export class Service extends BaseService {
stop(): void {}
}
3.3. 🌟 import 语句:精准导入与类型隔离
3.3.1. 导入默认导出与命名导出
导入命名导出
typescript
import { add, Config } from "./math";
const result = add(1, 2); // 合法
const config: Config = { precision: 2 }; // 合法
导入默认导出
typescript
import Calculator from "./calculator";
const calc = new Calculator();
console.log(calc.multiply(3, 4)); // 12
混合导入
typescript
import Calculator, { add } from "./module"; // 同时导入默认和命名导出
3.3.2. 类型导入的 type
语法
区分值和类型
typescript
// 导入值和类型
import { User } from "./types"; // 同时导入类型和值(若存在)
import type { User } from "./types"; // 仅导入类型,不打包到 JS
TIP
import type
可避免打包时引入无用的运行时代码。- 类型导入无需
declare
,直接使用接口/类型别名。
3.3.3. 坑位警示:默认导出的陷阱
陷阱 1:命名错误
typescript
// 错误示例:将默认导出命名为接口
import User from "./user"; // ❌ 默认导出是类或函数,不能直接当类型使用
// 正确写法:
import User, { UserType } from "./user";
// UserType 是接口,User 是默认导出的类
陷阱 2:类型与值冲突
typescript
// user.ts
export default class User {}
export type UserConfig = { /* ... */ };
// 其他文件
import User, { UserConfig } from "./user"; // User 是类,UserConfig 是类型
注意
默认导出的名称不能与命名导出的类型重名!
typescript
// 错误示例:
export default class User {}
export type User = "admin" | "guest"; // ❌ 类型与默认导出冲突
知识回顾
- 声明文件
- 通过
.d.ts
为 JavaScript 添加类型,支持全局变量和模块声明。 - 使用
declare
关键字定义类型,无需实现代码。
- 通过
- export 语法
- 命名导出:
export function/class/variable
。 - 默认导出:
export default
。 - 接口支持合并扩展。
- 命名导出:
- import 语法
- 区分默认导出和命名导出的语法。
- 使用
import type
隔离类型导入,避免打包冗余。
课后练习
(单选)以下哪种写法可以正确导入默认导出的
Calculator
类? A.import Calculator from "./calculator";
B.import { Calculator } from "./calculator";
C.import * as Calculator from "./calculator";
D.import { default as Calculator } from "./calculator";
(填空)为以下 JavaScript 函数编写类型声明:
javascript// math.js export function subtract(a, b) { return a - b; }
声明文件代码:
typescriptexport declare function subtract(____, ____): ____;
(编码)修复以下代码的类型错误:
typescript// user.ts export default class User { id!: number; } export type UserRoles = "admin" | "user"; // main.ts import User, { UserRoles } from "./user"; // ❌ 类型冲突 // 请修改导入语句