TypeScript 核心特性详解:枚举、泛型、命名空间和模块

更新日期: 2025-11-16 阅读: 25 标签: 特性

TypeScript 为 JavaScript 带来了强大的类型系统,让大型项目开发更加稳健。今天我们来深入理解枚举、泛型、命名空间和模块这几个核心概念。


枚举:给数值起个好名字

枚举是 TypeScript 中的特殊数据类型,让我们可以为数值设置有意义的名字。

数值枚举

enum Direction { 
  Up = 1,    // 从1开始
  Down,      // 自动变成2
  Left,      // 自动变成3
  Right      // 自动变成4
}

// 使用枚举
let move: Direction = Direction.Up;
console.log(move); // 输出: 1

如果不指定初始值,枚举会从0开始自动递增:

enum Status {
  Pending,    // 0
  Approved,   // 1  
  Rejected    // 2
}

字符串枚举

enum Response {
  Yes = "YES",
  No = "NO"
}

let answer: Response = Response.Yes;
console.log(answer); // 输出: "YES"

字符串枚举不会自动递增,每个成员都必须初始化。

常量枚举

使用 const enum 可以创建常量枚举,TypeScript 会在编译时进行优化:

const enum Size {
  Small,
  Medium,
  Large
}

// 编译后直接使用数值,不会生成额外的JavaScript代码
let mySize = Size.Medium; // 编译为: let mySize = 1;

异构枚举(不推荐)

TypeScript 支持数字和字符串混用的枚举,但通常不建议这样做:

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES"
}

这种混合使用会增加代码的复杂性,建议保持枚举成员类型一致。

枚举作为类型

枚举成员可以作为类型使用:

enum ShapeKind {
  Circle,
  Square,
}

interface Circle {
  kind: ShapeKind.Circle;  // 必须是ShapeKind.Circle
  radius: number;
}

interface Square {
  kind: ShapeKind.Square;  // 必须是ShapeKind.Square
  sideLength: number;
}

function createCircle(radius: number): Circle {
  return {
    kind: ShapeKind.Circle,  // 正确
    radius: radius
  };
}

泛型:编写可复用的类型安全代码

泛型让我们可以创建可重用的组件,同时保持类型安全。

函数泛型

// 基础泛型函数
function identity<T>(arg: T): T {
  return arg;
}

// 使用泛型函数
let numberResult = identity<number>(42);        // 类型为 number
let stringResult = identity<string>("hello");   // 类型为 string
let inferredResult = identity(true);            // TypeScript 自动推断类型为 boolean

接口泛型

// 泛型接口
interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

// 使用泛型接口
let numberPair: KeyValuePair<number, string> = {
  key: 1,
  value: "one"
};

let stringPair: KeyValuePair<string, boolean> = {
  key: "isActive",
  value: true
};

类泛型

class Container<T> {
  private value: T;
  
  constructor(value: T) {
    this.value = value;
  }
  
  getValue(): T {
    return this.value;
  }
  
  setValue(newValue: T): void {
    this.value = newValue;
  }
}

// 使用泛型类
let numberContainer = new Container<number>(100);
console.log(numberContainer.getValue()); // 输出: 100

let stringContainer = new Container<string>("TypeScript");
console.log(stringContainer.getValue()); // 输出: "TypeScript"

泛型约束

有时候我们需要限制泛型的类型范围:

// 要求泛型必须有 length 属性
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): void {
  console.log(arg.length);
}

logLength("hello");     // 正确,字符串有length属性
logLength([1, 2, 3]);   // 正确,数组有length属性
logLength(42);          // 错误,数字没有length属性

实际应用示例

// api响应包装器
interface ApiResponse<T> {
  success: boolean;
  data: T;
  message?: string;
}

// 用户数据接口
interface User {
  id: number;
  name: string;
  email: string;
}

// 使用泛型接口
function fetchUser(userId: number): ApiResponse<User> {
  // 模拟API调用
  return {
    success: true,
    data: {
      id: userId,
      name: "张三",
      email: "zhangsan@example.com"
    }
  };
}

// 分页数据
interface PaginatedList<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
}

type UserList = PaginatedList<User>;

命名空间:代码组织的传统方式

命名空间帮助我们将相关代码组织在一起,避免全局命名冲突。

创建命名空间

namespace MathUtils {
  export const PI = 3.14159;
  
  export function calculateArea(radius: number): number {
    return PI * radius * radius;
  }
  
  export function calculateCircumference(radius: number): number {
    return 2 * PI * radius;
  }
}

// 使用命名空间
console.log(MathUtils.PI); // 输出: 3.14159
let area = MathUtils.calculateArea(5); // 输出: 78.53975

注意:需要使用 export 关键字才能在命名空间外部访问成员。

嵌套命名空间

namespace Company {
  export namespace HR {
    export function hireEmployee(name: string): void {
      console.log(`雇佣员工: ${name}`);
    }
  }
  
  export namespace Finance {
    export function calculateSalary(hours: number): number {
      return hours * 50;
    }
  }
}

// 使用嵌套命名空间
Company.HR.hireEmployee("李四");
let salary = Company.Finance.calculateSalary(40);

命名空间的使用场景

  1. 第三方库开发

namespace MyChartLibrary {
  export function createLineChart(data: number[]): void {
    // 创建折线图逻辑
  }
  
  export function createBarChart(data: number[]): void {
    // 创建柱状图逻辑
  }
}

// 使用库
MyChartLibrary.createLineChart([1, 2, 3, 4]);
  1. 与旧代码兼容

// 旧有的全局变量
declare var LegacyApp: any;

namespace ModernApp {
  export function initialize(): void {
    // 与旧代码交互
    if (LegacyApp) {
      LegacyApp.setup();
    }
  }
}

模块:现代代码组织方式

模块是当前推荐的代码组织方式,每个文件都是一个模块。

创建和使用模块

math.ts

export const PI = 3.14159;

export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}

// 默认导出
export default class Calculator {
  static square(x: number): number {
    return x * x;
  }
}

app.ts

// 命名导入
import { PI, add, multiply } from './math';

// 默认导入
import Calculator from './math';

// 使用导入的功能
console.log(PI); // 输出: 3.14159
let sum = add(5, 3); // 输出: 8
let squared = Calculator.square(4); // 输出: 16

模块重命名

// 重命名导入
import { PI as圆周率, add as 相加 } from './math';

console.log(圆周率); // 输出: 3.14159
let 结果 = 相加(2, 3); // 输出: 5

批量导入

// 导入所有导出
import * as MathFunctions from './math';

console.log(MathFunctions.PI); // 输出: 3.14159
let product = MathFunctions.multiply(4, 5); // 输出: 20

命名空间 vs 模块:如何选择

主要区别

特性命名空间模块
作用域全局作用域文件作用域
文件组织通常在同一文件中跨文件组织
依赖管理需要手动管理自动处理依赖
现代性传统方式现代标准

使用建议

使用模块的情况:

  • 新项目开发

  • 需要清晰的依赖关系

  • 与现代构建工具配合

  • 代码需要树摇优化

使用命名空间的情况:

  • 维护旧项目

  • 开发全局可用的库

  • 需要将多个文件合并输出

  • 与现有的命名空间代码交互

实际项目结构示例

src/
├── models/           // 数据模型
│   ├── user.ts
│   └── product.ts
├── services/         // 业务逻辑
│   ├── api.ts
│   └── auth.ts
├── utils/           // 工具函数
│   └── helpers.ts
└── app.ts          // 入口文件

user.ts(模块示例)

export interface User {
  id: number;
  name: string;
  email: string;
}

export function createUser(name: string, email: string): User {
  return {
    id: Date.now(), // 简单ID生成
    name,
    email
  };
}

api.ts(模块示例)

import { User } from '../models/user';

export class ApiService {
  static async fetchUser(userId: number): Promise<User> {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  }
  
  static async saveUser(user: User): Promise<void> {
    await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(user)
    });
  }
}

总结

TypeScript 提供了多种代码组织方式:

  • 枚举:让数值更有意义,提高代码可读性

  • 泛型:创建可重用且类型安全的组件

  • 命名空间:传统的代码组织方式,适合特定场景

  • 模块:现代代码组织标准,推荐在新项目中使用

在实际开发中,建议优先使用模块来组织代码,配合枚举和泛型来增强类型安全。只有在需要与旧代码兼容或开发特定类型的库时,才考虑使用命名空间。

掌握这些特性可以帮助你编写更健壮、更易维护的 TypeScript 代码,为大型项目开发打下坚实基础。

本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!

链接: https://fly63.com/article/detial/13187

CSS新特性contain,控制页面的重绘与重排

contain 属性允许我们指定特定的 DOM 元素和它的子元素,让它们能够独立于整个 DOM 树结构之外。目的是能够让浏览器有能力只对部分元素进行重绘、重排,而不必每次都针对整个页面。

Html5、Css3、ES6的新特性

Html5的新特性语义化标签:有利于SEO,有助于爬虫抓取更多的有效信息,爬虫是依赖于标签来确定上下文和各个关键字的权重。表单新特性,多媒体视频(video)和音频(audio)

ES6新特性--var、let、const

var不存在块级作用域,具有变量提升机制。 let和const存在块级作用域,不存在变量提升。在同一作用域内只能声明一次。const在声明时需要赋值且无法修改,但如果常量是对象,则对象的属性可以修改。

Js即将到来的3个新特性

Optional Chaining(可选链式调用);Nullish coalescing(空值合并);Pipeline operator(管道运算符)通过三个函数对字符串进行处理;

Angular 8的新特性介绍

在今天早些时候Angular团队发布了8.0.0稳定版。其实早在NgConf 2019大会上,演讲者就已经提及了从工具到差分加载的许多内容以及更多令人敬畏的功能。下面是我对8.0.0一些新功能的简单介绍,希望可以帮助大家快速了解新版本

使用 React 要懂的 Js特性

与我使用的其他框架相比,我最喜欢 React 的原因之一就是它对 JavaScript 的暴露程度。没有模板DSL( JSX 编译为合理的 JavaScript),组件 API 只是通过添加 React Hooks 变得更简单,并且该框架为解决的核心 UI 问题提供非常少的抽象概念

ES2019 新特性汇总

最近 ECMAScript2019,最新提案完成:tc39 Finished Proposals,我这里也是按照官方介绍的顺序进行整理,如有疑问,可以查看官方介绍啦~另外之前也整理了 《ES6/ES7/ES8/ES9系列》,可以一起看哈。

Js的用途和特性

JavaScript 最初的目的是为了“赋予网页生命”。这种编程语言我们称之为脚本。它们可以写在 HTML 中,在页面加载的时候会自动执行。脚本作为纯文本存在和执行。它们不需要特殊的准备或编译即可运行。

十个超级实用的 JS 特性

你可能刚上手 JavaScript,或者只是曾经偶尔用过。不管怎样,JavaScript 改变了很多,有些特性非常值得一用。 这篇文章介绍了一些特性,在我看来,一个严肃的 JavaScript 开发者每天都多多少少会用到这些特性

解密HTTP/2与HTTP/3 的新特性

HTTP/2 相比于 HTTP/1.1,可以说是大幅度提高了网页的性能,只需要升级到该协议就可以减少很多之前需要做的性能优化工作,当然兼容问题以及如何优雅降级应该是国内还不普遍使用的原因之一。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!