TypeScript 类型系统初探

87 阅读2分钟

TypeScript 的核心价值在于其强大的类型系统,它能够显著提高代码的可靠性、可维护性和开发体验。本文将全面解析 TypeScript 中的各种类型,帮助您构建更加健壮的应用程序。

TypeScript 类型系统概览

TypeScript 的类型系统可以归纳为以下几个主要类别:

类型类别主要类型特点
基本类型stringnumberboolean 等JavaScript 的基础值类型
复合类型数组、元组、对象、函数由基础类型组合而成
特殊类型anyunknownvoidnever特殊场景使用的类型
结构类型接口、类、字面量描述对象结构
高级类型联合、交叉、条件类型等提供高级类型操作

现在让我们深入探索每一类类型。

一、基本原始类型 (Primitive Types)

这些类型对应 JavaScript 的基本值类型:

// 字符串类型
let name: string = "Alice";
let greeting: string = `Hello, ${name}!`;

// 数字类型 (包含整数和浮点数)
let age: number = 30;
let temperature: number = 24.5;

// 布尔类型
let isActive: boolean = true;
let hasPermission: boolean = false;

// null 和 undefined 类型
let emptyValue: null = null;
let notSet: undefined = undefined;

// Symbol 类型 (ES2015+)
const uniqueID: symbol = Symbol("uid");

特殊基本类型:bigint

处理超出 Number 安全整数限制的大整数:

// bigint 类型 (ES2020+)
const hugeNumber: bigint = BigInt(Number.MAX_SAFE_INTEGER) + BigInt(1);

二、数组与元组类型 (Collection Types)

数组类型 (Arrays)

TypeScript 提供了多种声明数组类型的语法:

let numbers: number[] = [1, 2, 3]; // 推荐语法
let colors: Array<string> = ["red", "green", "blue"]; // 泛型语法

// 只读数组
const readonlyColors: ReadonlyArray<string> = ["red", "blue"];
// readonlyColors.push("green"); // 错误:只读数组无法修改

元组类型 (Tuples)

固定长度和类型的数组:

// 基本元组
let userInfo: [string, number] = ["Alice", 30];

// 元组解构
const [name, age] = userInfo;

// 可选元素的元组
type HttpResponse = [
  number,          // 状态码
  string,          // 状态消息
  object?,         // 可选的数据对象 (TypeScript 4.0+)
  ...string[]      // 可变的附加信息
];

const response: HttpResponse = [200, "OK", { data: "success" }, "additional info"];

常量元组 (as const)

使用 as const 创建不可变元组:

const rgb = ["red", "green", "blue"] as const;
// rgb[0] = "yellow"; // 错误:只读属性

三、对象类型与接口 (Object Types & Interfaces)

对象类型注解

// 内联对象类型
let user: { name: string; age: number; isAdmin?: boolean } = {
  name: "Bob",
  age: 25
};

接口 - 定义对象结构

// 基本接口
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // 可选属性
  readonly joinDate: Date; // 只读属性
}

// 使用接口
const newUser: User = {
  id: 1,
  name: "Alice",
  email: "alice@example.com",
  joinDate: new Date()
};

// 接口继承
interface Admin extends User {
  permissions: string[];
  deleteUser(): void;
}

索引签名

处理动态键名:

interface DynamicObject {
  [key: string]: number;
}

const scores: DynamicObject = {
  math: 95,
  science: 90
};

四、函数类型 (Function Types)

函数声明和参数类型

// 具名函数
function calc(a: number, b: number): number {
  return a + b;
}

// 箭头函数
const multiply: (x: number, y: number) => number = (x, y) => x * y;

// 可选参数和默认值
function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

// 剩余参数
function log(...args: (string | number)[]): void {
  console.log(args.join(", "));
}

函数接口

interface SearchFunction {
  (source: string, keyword: string): boolean;
}

const search: SearchFunction = (src, key) => src.includes(key);

五、联合与交叉类型 (Union & Intersection Types)

联合类型 (|)

表示多种可能的类型:

type ID = string | number;

function printId(id: ID) {
  if (typeof id === "string") {
    console.log(`ID: ${id.toUpperCase()}`);
  } else {
    console.log(`ID: ${id.toFixed(2)}`);
  }
}

交叉类型 (&)

组合多个类型:

interface Name {
  name: string;
}

interface Contact {
  email: string;
  phone: string;
}

type Employee = Name & Contact;

const alice: Employee = {
  name: "Alice",
  email: "alice@company.com",
  phone: "123-456-7890"
};

六、字面量类型 (Literal Types)

固定值的类型:

// 字符串字面量类型
type Direction = "north" | "south" | "east" | "west";
const move: Direction = "north";

// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
const roll: DiceRoll = 4;

// 布尔字面量类型
type IsTrue = true;
const value: IsTrue = true;

七、枚举类型 (Enums)

定义命名常量集合:

// 数字枚举
enum UserRole {
  Guest,      // 0
  Member,     // 1
  Admin,      // 2
  SuperAdmin  // 3
}

// 字符串枚举
enum HttpMethod {
  GET = "GET",
  POST = "POST",
  PUT = "PUT",
  DELETE = "DELETE"
}

// 常量枚举(编译时内联)
const enum LogLevel {
  Error,
  Warn,
  Info,
  Debug
}

八、特殊类型

any:最灵活的类型(谨慎使用)

let flexible: any = "I can be anything!";
flexible = 42; // 允许
flexible = true; // 允许

unknown:类型安全的 any 替代

let input: unknown = getUserInput(); // 来源未知的值

if (typeof input === "string") {
  console.log(input.toUpperCase()); // 类型已收窄
}

void 和 never

// void - 表示没有返回值的函数
function logMessage(): void {
  console.log("This returns nothing");
}

// never - 表示永远不会返回的函数
function error(message: string): never {
  throw new Error(message);
}

// never 也用于处理不可能的情况
type Possible = string | number;

function check(value: Possible) {
  if (typeof value === "string") {
    // 处理字符串
  } else if (typeof value === "number") {
    // 处理数字
  } else {
    // 这里 value 的类型是 never
    // 确保处理了所有可能的情况
  }
}

九、类型别名 (Type Aliases)

创建自定义类型名称:

// 基本类型别名
type Point = {
  x: number;
  y: number;
};

// 函数类型别名
type Calculator = (a: number, b: number) => number;

// 泛型类型别名
type Pair<T> = [T, T];
const numbers: Pair<number> = [1, 2];

接口 vs 类型别名

特性接口 (interface)类型别名 (type)
扩展方式extends交叉类型 (&)
合并声明✅ 支持接口合并❌ 不支持
实现类✅ 可用于类实现❌ 不能实现
复杂类型❌ 有限支持✅ 支持联合、元组等
泛型✅ 支持✅ 支持
描述对象✅ 主要用途✅ 可以描述
描述其他类型❌ 不支持✅ 支持各种类型

十、高级类型

条件类型 (Conditional Types)

根据条件选择类型:

type NonNullable<T> = T extends null | undefined ? never : T;

// 使用示例
type ValidString = NonNullable<string | null>;  // string

映射类型 (Mapped Types)

转换现有类型:

// 所有属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 所有属性变为只读
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

模板字面量类型 (Template Literal Types)

type Color = "red" | "blue" | "green";
type Size = "sm" | "md" | "lg";

type ButtonClass = `btn-${Color}-${Size}`;
// "btn-red-sm" | "btn-red-md" | "btn-red-lg" | "btn-blue-sm" | ...

索引访问和键类型

interface Person {
  name: string;
  age: number;
  address: {
    city: string;
    zipCode: string;
  };
}

type PersonKeys = keyof Person; // "name" | "age" | "address"
type AgeType = Person["age"]; // number
type CityType = Person["address"]["city"]; // string

类型实践:最佳实践与建议

  1. 避免过度使用 any:优先使用 unknown 和类型断言替代

  2. 利用类型推断:让 TypeScript 自动推断类型,减少手动注解

  3. 使用 const 断言:创建不可变值和字面量类型

    const colors = ["red", "green"] as const;
    
  4. 优先选择联合类型而非枚举:更符合 TypeScript 的声明式风格

    // 优于枚举
    const UserRole = {
      Admin: "admin",
      User: "user"
    } as const;
    type UserRole = typeof UserRole[keyof typeof UserRole];
    
  5. 使用严格模式:开启 strict 编译器标志以获得更强的类型检查

拥抱类型的力量

TypeScript 的类型系统提供了构建可靠应用程序的坚实基础:

  • 开发阶段:通过自动补全和实时错误检查提升开发效率
  • 重构阶段:安全地进行大型重构,避免引入错误
  • 调试阶段:减少运行时错误,提高代码可靠性
  • 协作阶段:清晰表达代码意图,提高团队协作效率

“类型不是约束,而是表达意图的方式 - 它们使代码更具可读性、可维护性和自文档化。” - TypeScript 理念

开始使用这些类型技术,您将体验到更少的错误、更少的调试时间和更愉悦的开发体验。随着 TypeScript 的发展,类型系统也在不断进化,持续关注新特性将使您的代码保持前沿!