类型导入与导出
类型导入与导出的基本概念
TypeScript中的类型导入与导出机制允许开发者将类型定义模块化,并在不同文件间共享。这种机制与JavaScript的模块系统紧密集成,但增加了类型层面的支持。通过import
和export
关键字,可以像处理值一样处理类型,使得代码组织更加清晰。
// types.ts
export interface User {
id: number;
name: string;
}
export type Status = 'active' | 'inactive';
默认导出与命名导出
类型导出支持两种主要形式:命名导出和默认导出。命名导出允许从模块导出多个类型,而默认导出则适用于模块主要类型的场景。
// 命名导出示例
export interface Product {
sku: string;
price: number;
}
export function calculateTax(price: number): number {
return price * 0.1;
}
// 默认导出示例
export default class ShoppingCart {
items: Product[] = [];
}
导入时对应不同的语法:
// 导入命名导出
import { Product, calculateTax } from './models';
// 导入默认导出
import ShoppingCart from './ShoppingCart';
类型专用导入语法
TypeScript提供了专门用于类型导入的语法,可以明确区分类型导入和值导入。这在某些编译优化场景下很有用。
import type { User } from './types';
import { type User, getUser } from './api';
// 纯类型导入在编译后会完全移除
重新导出模式
模块可以重新导出其他模块的类型,这在创建聚合模块或库的入口文件时特别有用。
// utils/index.ts
export { formatDate } from './date';
export { parseQuery } from './url';
export type { QueryParams } from './url'; // 只重新导出类型
动态类型导入
TypeScript 4.5+支持类型化的动态导入,允许在保持类型安全的同时使用动态模块加载。
const loadUserModule = async () => {
const userModule = await import('./user');
const user: userModule.User = { id: 1, name: 'Alice' };
return user;
};
命名空间中的类型导出
在命名空间中使用导出时,类型和值可以混合导出,但需要注意它们的不同行为。
namespace Geometry {
export interface Point {
x: number;
y: number;
}
export class Circle {
constructor(public center: Point, public radius: number) {}
}
}
// 使用导出的类型和类
const center: Geometry.Point = { x: 0, y: 0 };
const circle = new Geometry.Circle(center, 10);
类型导出与泛型
泛型类型也可以被导出,使用时需要提供类型参数。
// data-structures.ts
export interface List<T> {
items: T[];
add(item: T): void;
get(index: number): T | undefined;
}
// 使用导出的泛型接口
import { List } from './data-structures';
const numberList: List<number> = {
items: [1, 2, 3],
add(item) { this.items.push(item); },
get(index) { return this.items[index]; }
};
条件类型与导出
高级类型特性如条件类型也可以被导出,增强类型系统的表达能力。
// type-utils.ts
export type NonNullable<T> = T extends null | undefined ? never : T;
export type Flatten<T> = T extends Array<infer U> ? U : T;
// 使用条件类型
import { NonNullable, Flatten } from './type-utils';
type ValidString = NonNullable<string | null>; // string
type ElementType = Flatten<string[][]>; // string[]
类型导出与第三方声明
处理第三方库类型时,经常需要扩展或覆盖已有的类型定义。
// react-extensions.d.ts
import * as React from 'react';
declare module 'react' {
export interface Component {
$customMethod?: () => void;
}
}
// 使用扩展后的类型
const component: React.Component = /* ... */;
component.$customMethod?.();
类型导出与配置
tsconfig.json
中的配置会影响类型导入导出的行为,特别是moduleResolution
和paths
等选项。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@models/*": ["src/models/*"]
}
}
}
然后可以这样导入:
import { User } from '@models/types';
类型导出与性能考量
大量使用类型导入导出会影响编译性能。TypeScript 3.8引入的import type
可以帮助减少不必要的类型依赖。
// 避免将类型作为值导入
import type { BigConfig } from './config';
// 运行时不需要的类型会被移除
function configure(config: BigConfig) {
// ...
}
类型导出与模块系统
不同的模块系统(CommonJS/ESM)对类型导出的处理略有不同,特别是在类型与值混合导出的场景。
// CommonJS风格导出
interface DbConfig {
host: string;
port: number;
}
const connect = (config: DbConfig) => { /*...*/ };
export = {
connect,
DbConfig // 这样导出类型在CommonJS中需要额外处理
};
类型导出与工具链集成
构建工具如webpack、rollup等对类型导入导出的支持程度不同,可能需要额外插件。
// 使用webpack的require.ensure保持类型
declare function requireEnsure<T>(path: string): Promise<T>;
const loadModule = async () => {
const { default: Component } = await requireEnsure<{
default: React.ComponentType
}>('./AsyncComponent');
return Component;
};
类型导出与测试
在测试环境中处理类型导入导出时,可能需要模拟类型或创建测试专用的类型变体。
// 测试专用的类型扩展
interface TestUser extends User {
testData?: string;
}
function createTestUser(overrides: Partial<TestUser>): TestUser {
return {
id: 1,
name: 'Test',
...overrides
};
}
类型导出与文档生成
工具如TypeDoc可以基于类型导出自动生成API文档,保持文档与类型定义的同步。
/**
* 表示应用程序用户
* @remarks
* 包含用户的基本信息
*/
export interface User {
/** 用户唯一标识 */
id: number;
/** 用户显示名称 */
name: string;
}
类型导出与代码拆分
在代码拆分场景下,类型导入导出需要考虑类型可见性和模块边界。
// 使用动态导入保持类型安全
async function loadComponent() {
const { default: Chart } = await import(
/* webpackChunkName: "chart" */ './ChartComponent'
);
// Chart组件类型会被正确推断
return <Chart data={[]} />;
}
类型导出与monorepo
在monorepo项目中,类型导入导出需要处理跨包引用,通常使用项目引用(project references)功能。
// tsconfig.json
{
"compilerOptions": {
"composite": true
},
"references": [
{ "path": "../shared-types" }
]
}
然后可以跨项目导入类型:
import { SharedType } from '@company/shared-types';
类型导出与声明合并
导出的接口可以与其他位置的同名声明的接口合并,这在扩展第三方类型时特别有用。
// user-extensions.ts
import { User } from './models';
declare module './models' {
interface User {
lastLogin?: Date;
permissions: string[];
}
}
// 使用合并后的类型
const user: User = {
id: 1,
name: 'Alice',
permissions: ['read', 'write'] // 新增的属性
};
类型导出与可变模块
处理可能变化的模块类型时,需要使用TypeScript的const
断言和精确类型导入。
// 使用const断言固定导入类型
import { endpoints } from './config';
type Endpoints = typeof endpoints;
function getEndpoint(key: keyof Endpoints): string {
return endpoints[key];
}
类型导出与性能优化
对于大型项目,合理组织类型导入导出可以显著影响编译性能和开发者体验。
// 使用barrel文件组织类型导出
// models/index.ts
export * from './user';
export * from './product';
export * from './order';
// 使用时可以集中导入
import { User, Product } from '../models';
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn