阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 类型导入与导出

类型导入与导出

作者:陈川 阅读数:61122人阅读 分类: TypeScript

类型导入与导出的基本概念

TypeScript中的类型导入与导出机制允许开发者将类型定义模块化,并在不同文件间共享。这种机制与JavaScript的模块系统紧密集成,但增加了类型层面的支持。通过importexport关键字,可以像处理值一样处理类型,使得代码组织更加清晰。

// 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中的配置会影响类型导入导出的行为,特别是moduleResolutionpaths等选项。

{
  "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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌