TypeScript 类型系统:接口、泛型与高级类型

FreeGuideOnline 最新 2026-06-15

TypeScript 类型系统:接口、泛型与高级类型

TypeScript 的核心优势在于其强大的类型系统,它能在编译阶段捕捉错误,提升代码的健壮性。本文将聚焦于类型系统中最重要的三个概念:接口泛型高级类型,帮助你从基础走向进阶。

1. 接口:定义数据的形状

接口(Interface)用于为对象、函数、类等定义“契约”,描述其应有的结构。通过接口,你可以确保代码中使用到的数据结构符合预期。

1.1 基本接口

使用 interface 关键字定义一个对象的形状,包含属性和其类型。

interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `你好,${user.name},你今年 ${user.age} 岁。`;
}

const user: User = { name: "小明", age: 25 };
console.log(greet(user));

1.2 可选属性与只读属性

  • 可选属性:在属性名后加 ?,表示该属性可以不存在。
  • 只读属性:使用 readonly 修饰,初始化后不可修改。
interface Config {
  readonly id: number;
  url: string;
  timeout?: number; // 可选属性
}

const config: Config = { id: 1, url: "https://api.example.com" };
// config.id = 2; // 错误!只读属性不可修改

1.3 函数类型接口

接口也可以描述函数类型:定义参数列表和返回值类型。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

const mySearch: SearchFunc = (src, sub) => {
  return src.includes(sub);
};

1.4 接口继承

接口可以通过 extends 继承另一个接口,从而复用其成员,并可添加新成员。

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
  bark(): void;
}

const myDog: Dog = {
  name: "旺财",
  breed: "金毛",
  bark() {
    console.log("汪汪!");
  }
};

1.5 接口与类型别名的区别(简述)

接口和类型别名(type)很相似,但接口更擅长定义对象形状,具有更好的扩展性(可被继承和合并),而类型别名更灵活,能表示联合类型、交叉类型等。在大多数场景下,你可以根据个人/团队偏好选择,但复杂对象优先考虑接口。


2. 泛型:可复用的类型组件

泛型(Generics)允许你编写不预先指定具体类型的代码,而是在使用时再确定类型,从而实现类型安全的复用。

2.1 泛型函数

在函数名后使用 <T> 定义类型变量,T 可以用于参数、返回值,保持它们之间的关联。

function identity<T>(arg: T): T {
  return arg;
}

let output = identity<string>("hello"); // 显式指定类型
let output2 = identity(42); // 类型推断为 number

2.2 泛型接口与类

接口也能使用泛型,增强其灵活性。

interface Box<T> {
  value: T;
}

const numberBox: Box<number> = { value: 10 };
const stringBox: Box<string> = { value: "类型安全" };

泛型类类似:

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

const numberStack = new Stack<number>();
numberStack.push(1);

2.3 泛型约束

使用 extends 限制泛型参数必须满足某种形状,例如必须拥有某个属性。

interface Lengthwise {
  length: number;
}

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

logLength("hello"); // 字符串有 length
logLength([1, 2, 3]); // 数组有 length
// logLength(10); // 错误!number 没有 length

2.4 在泛型中使用类型参数

一个类型参数可以约束另一个类型参数,常用于关联对象的属性。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person = { name: "小明", age: 20 };
getProperty(person, "name"); // 类型为 string
// getProperty(person, "address"); // 错误!"address" 不存在

3. 高级类型:构造更复杂的类型

随着应用复杂度的增加,你需要更灵活的类型构造手段。以下是一些常见的高级类型。

3.1 联合类型与交叉类型

  • 联合类型(|:值可以是多种类型之一。
  • 交叉类型(&:将多个类型合并为一个,拥有所有类型成员。
type ID = string | number; // 联合类型
let userId: ID = 1024; // 可以是数字
userId = "abc123"; // 也可以是字符串

interface Nameable {
  name: string;
}
interface Ageable {
  age: number;
}
type Person = Nameable & Ageable; // 交叉类型
const person: Person = { name: "小明", age: 25 };

3.2 类型别名与字面量类型

类型别名(type)可给任何类型起个新名字。字面量类型允许你使用具体的字符串或数字作为类型,常与联合类型结合使用。

type Direction = "north" | "south" | "east" | "west";
function move(direction: Direction) { /* ... */ }
move("north");
// move("up"); // 错误!只能使用指定的字面量

3.3 类型守卫与类型断言

  • 类型守卫:运行时检查以在作用域内缩小类型范围,如 typeofinstanceof 或自定义函数。
  • 类型断言:你比编译器更清楚类型时,使用 as 或尖括号语法告知类型。
function formatValue(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // 此处 value 被缩窄为 string
  }
  return value.toFixed(2); // 此处 value 为 number
}

// 类型断言
const someValue: unknown = "hello";
const strLength: number = (someValue as string).length;

3.4 条件类型与映射类型(简介)

  • 条件类型:根据条件选择不同的类型,类似三元运算符。T extends U ? X : Y
  • 映射类型:从已有类型创建新类型,对每个属性进行转换,常配合 keyof 使用。
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"

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

interface Todo {
  title: string;
  description: string;
}
const todo: Readonly<Todo> = {
  title: "学习 TypeScript",
  description: "深入理解类型系统"
};
// todo.title = "修改"; // 错误,只读

总结

TypeScript 的类型系统通过接口定义形状,泛型实现可复用组件,高级类型提供灵活的构造能力,三者结合能够编写出更加健壮、可维护的代码。作为初学者,建议:

  1. 先用接口描述对象和函数的形状,熟悉可选/只读等基础特性。
  2. 在需要处理通用数据结构时引入泛型,渐进式扩展。
  3. 随着项目复杂化,逐步学习联合类型、字面量类型等高级模式。

掌握这些类型工具,你将能充分发挥 TypeScript 的威力。