Vanson's Eternal Blog

TypeScript 中的面向Interface接口编程

Typescript interface.png
Published on
/15 mins read/---

interface 是 TypeScript 中一个非常强大的工具,它不仅用于定义对象的结构,还可以用于定义函数类型、混合类型等。

在实际开发中,interface 被广泛应用于前端框架(如 React 和 Angular)和后端开发中,帮助开发者编写更清晰、更健壮的代码。

数据模型定义

interface 是 TypeScript 中用于定义对象结构的一种方式,它确保对象符合特定的属性和类型。接口可以看作是一种类型约定,用于确保对象的结构符合预期

 
// 定义用户实体接口
interface User {
  id: string;
  name: string;
  email?: string;          // 可选属性‌:ml-citation{ref="1" data="citationList"}
  readonly createdAt: Date; // 只读属性‌:ml-citation{ref="3" data="citationList"}
}
 
// API 响应结构
interface ApiResponse<T> {
  code: number;
  data: T;
  message?: string;
}
 
// 使用泛型接口
const response: ApiResponse<User> = {
  code: 200,
  data: { id: "1", name: "Alice", createdAt: new Date() }
};
 
 
  • 接口通过强制属性类型和可选性约束数据结构‌。
  • 泛型接口 ApiResponse<T> 实现响应模板复用,适应不同数据实体‌。

可选属性

接口中的属性可以设置为可选的,即不是必需的。使用 ? 符号可以定义可选属性

 
interface IPerson {
  name: string;
  age?: number;
}
 
let person1: IPerson = {
  name: "Alice"
};
 
let person2: IPerson = {
  name: "Bob",
  age: 20
};
 
console.log(person1.name); // 输出: Alice
console.log(person2.age);  // 输出: 20
 

只读属性

接口中的属性可以设置为只读的,即不能修改其值。使用 readonly 关键字可以定义只读属性。

 
interface IPoint {
  readonly x: number;
  readonly y: number;
}
 
let point: IPoint = {
  x: 10,
  y: 20
};
 
// point.x = 5; // 错误,无法修改只读属性
 

字符串索引签名

接口可以定义字符串索引签名,以支持通过字符串来索引对象的属性。

 
interface IDictionary {
  [key: string]: string;
}
 
let dict: IDictionary = {
  apple: "red",
  banana: "yellow"
};
 
console.log(dict.apple);  // 输出: red
console.log(dict.banana); // 输出: yellow
 

函数类型

接口可以描述函数类型,包括函数的参数和返回值类型。 这在定义回调函数或事件处理器时非常有用。

 
interface ICalculator {
  (a: number, b: number): number;
}
 
let add: ICalculator = function (a, b) {
  return a + b;
};
 
let subtract: ICalculator = function (a, b) {
  return a - b;
};
 
console.log(add(2, 3));    // 输出: 5
console.log(subtract(5, 2)); // 输出: 3
 

接口继承

接口可以通过继承其他接口来扩展自己的形状。

 
interface IShape {
  color: string;
}
 
interface ISquare extends IShape {
  sideLength: number;
}
 
let square: ISquare = {
  color: "red",
  sideLength: 10
};
 
console.log(square.color);     // 输出: red
console.log(square.sideLength); // 输出: 10
 

混合类型

接口可以描述具有多种类型的对象,称为混合类型。

 
interface Counter {
  (start: number): void;
  interval: number;
  reset(): void;
}
 
const counter: Counter = (function (start: number) {
  console.log("Counter started at", start);
} as Counter);
 
counter.interval = 1000;
counter.reset = () => {
  console.log("Counter reset");
};
 
counter(10); // 输出: Counter started at 10
console.log(counter.interval); // 输出: 1000
counter.reset(); // 输出: Counter reset
 

泛型接口

泛型接口允许你在接口中使用类型参数,从而创建更通用的接口。

 
interface Box<T> {
  contents: T;
  getContents(): T;
}
 
const stringBox: Box<string> = {
  contents: "TypeScript",
  getContents: function () {
    return this.contents;
  }
};
 
const numberBox: Box<number> = {
  contents: 42,
  getContents: function () {
    return this.contents;
  }
};
 
console.log(stringBox.getContents()); // 输出: TypeScript
console.log(numberBox.getContents()); // 输出: 42
 

工具类型

TypeScript 提供了一些内置的工具类型,用于常见的类型转换。

 
// Partial 工具类型
interface Person {
  name: string;
  age: number;
}
 
type PartialPerson = Partial<Person>;
const p: PartialPerson = { name: "Alice" };
 
// Required 工具类型
type RequiredPerson = Required<PartialPerson>;
const rp: RequiredPerson = { name: "Eve", age: 28 };
 
// Readonly 工具类型
type ReadonlyPerson = Readonly<Person>;
const rop: ReadonlyPerson = { name: "Mallory", age: 32 };
 
// Record 工具类型
type Fruit = "apple" | "banana" | "orange";
type FruitPrices = Record<Fruit, number>;
const prices: FruitPrices = {
  apple: 1.00,
  banana: 0.50,
  orange: 0.75
};
 
// Pick 工具类型
type NameAndAge = Pick<Person, "name" | "age">;
const na: NameAndAge = { name: "Carol", age: 40 };
 
// Omit 工具类型
type PersonWithoutAge = Omit<Person, "age">;
const pwa: PersonWithoutAge = { name: "Dan" };
 
console.log(p.name); // 输出: Alice
console.log(rp.age); // 输出: 28
console.log(prices.apple); // 输出: 1
console.log(na.name); // 输出: Carol
console.log(pwa.name); // 输出: Dan
 

工具类型(Utility Types)是一组内置的类型,用于在开发中进行常见的类型转换和操作。这些工具类型可以帮助你更灵活地处理类型,提高代码的可读性和可维护性。

Partial

Partial<T> 将类型 T 的所有属性都设为可选的。

 
interface Person {
  name: string;
  age: number;
}
 
type PartialPerson = Partial<Person>;
 
const person: PartialPerson = {
  name: "Alice"
};
 
console.log(person.name); // 输出: Alice
console.log(person.age);  // 输出: undefined
 

Readonly

Readonly<T> 将类型 T 的所有属性都设为只读的。

 
interface Person {
  name: string;
  age: number;
}
 
type ReadonlyPerson = Readonly<Person>;
 
const person: ReadonlyPerson = {
  name: "Alice",
  age: 30
};
 
// person.name = "Bob"; // 错误: 无法分配到 'name',因为它是只读属性。
 

Required

Required<T> 将类型 T 的所有可选属性都设为必需的。

 
interface Person {
  name: string;
  age?: number;
}
 
type RequiredPerson = Required<Person>;
 
const person: RequiredPerson = {
  name: "Alice",
  age: 30
};
 
console.log(person.name); // 输出: Alice
console.log(person.age);  // 输出: 30
 

Pick

Pick<T, K> 从类型 T 中选择指定的属性 K,形成一个新的类型。

interface Person {
  name: string;
  age: number;
  email: string;
}
 
type NameAndAge = Pick<Person, "name" | "age">;
 
const person: NameAndAge = {
  name: "Alice",
  age: 30
};
 
console.log(person.name); // 输出: Alice
console.log(person.age);  // 输出: 30
 

Omit

Omit<T, K> 从类型 T 中排除指定的属性 K,形成一个新的类型。

interface Person {
  name: string;
  age: number;
  email: string;
}
 
type PersonWithoutEmail = Omit<Person, "email">;
 
const person: PersonWithoutEmail = {
  name: "Alice",
  age: 30
};
 
console.log(person.name); // 输出: Alice
console.log(person.age);  // 输出: 30
 

Record

Record<K, T> 创建一个类型,其键为 K,值为 T

 
type StringArray = Record<number, string>;
 
const myArray: StringArray = {
  0: "Hello",
  1: "World"
};
 
console.log(myArray[0]); // 输出: Hello
console.log(myArray[1]); // 输出: World
 

Exclude

Exclude<T, U> 从类型 T 中排除所有可以分配给 U 的类型。

 
type T0 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d"
type T1 = Exclude<string | number | (() => void), Function>; // string | number
 

Extract

Extract<T, U> 从类型 T 中提取所有可以分配给 U 的类型。

 
type T0 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c"
type T1 = Extract<string | number | (() => void), Function>; // never
 

NonNullable

NonNullable<T> 从类型 T 中排除 null 和 undefined。

 
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]
 

ReturnType

ReturnType<T> 获取函数类型 T 的返回值类型。

 
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<(<T>() => T) extends (x: infer R) => infer U ? [R, U] : never>; // [unknown, unknown]
type T3 = ReturnType<any>; // any
type T4 = ReturnType<never>; // never
type T5 = ReturnType<string>; // Error
type T6 = ReturnType<Function>; // Error
 

InstanceType

InstanceType<T> 获取构造函数类型 T 的实例类型。

 
class C {
  x = 0;
  y = 0;
}
 
type T0 = InstanceType<typeof C>; // C
type T1 = InstanceType<any>; // any
type T2 = InstanceType<never>; // never
type T3 = InstanceType<string>; // Error
type T4 = InstanceType<Function>; // Error
 

Parameters

Parameters<T> 获取函数类型 T 的参数类型。

 
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
type T2 = Parameters<(s: string, n: number) => void>; // [string, number]
type T3 = Parameters<any>; // any
type T4 = Parameters<never>; // never
type T5 = Parameters<string>; // Error
type T6 = Parameters<Function>; // Error
 

ConstructorParameters

ConstructorParameters<T> 获取构造函数类型 T 的参数类型。

 
class C {
  constructor(x: string, y: number) {}
}
 
type T0 = ConstructorParameters<typeof C>; // [string, number]
type T1 = ConstructorParameters<any>; // any
type T2 = ConstructorParameters<never>; // never
type T3 = ConstructorParameters<string>; // Error
type T4 = ConstructorParameters<Function>; // Error
 

ThisParameterType

ThisParameterType<T> 获取函数类型 T 的 this 参数类型。

 
type T0 = ThisParameterType<() => void>; // void
type T1 = ThisParameterType<(this: void) => void>; // void
type T2 = ThisParameterType<(this: string) => void>; // string
type T3 = ThisParameterType<any>; // any
type T4 = ThisParameterType<never>; // never
type T5 = ThisParameterType<string>; // Error
type T6 = ThisParameterType<Function>; // Error
 

OmitThisParameter

OmitThisParameter<T> 从函数类型 T 中移除 this 参数。

 
type T0 = OmitThisParameter<() => void>; // () => void
type T1 = OmitThisParameter<(this: void) => void>; // () => void
type T2 = OmitThisParameter<(this: string) => void>; // () => void
type T3 = OmitThisParameter<any>; // any
type T4 = OmitThisParameter<never>; // never
type T5 = OmitThisParameter<string>; // Error
type T6 = OmitThisParameter<Function>; // Error
 

联合类型和交叉类型

联合类型

允许变量或参数同时属于多种类型之一。

语法‌:Type1 | Type2 | ... | TypeN

 
type Status = "success" | "error";           // 字面量联合
type ID = string | number;                   // 基础类型联合
type Data = string[] | { value: string };    // 复杂类型联合
 
 

核心特性‌

  • ‌类型范围‌:联合类型是类型集合的 ‌逻辑或(OR)‌ 关系。
  • ‌类型安全‌:只能访问所有联合类型共有的成员。
interface Dog { bark(): void }
interface Cat { meow(): void }
 
type Pet = Dog | Cat;
 
function doSomething(pet: Pet) {
  pet.bark();  // ❌ 错误:`bark` 不一定是共有成员
  if ("bark" in pet) {  // 类型守卫(Type Guard)
    pet.bark(); // ✅ 合法
  }
}
 
 

应用场景‌

处理多种输入格式。

 
type InputValue = string | number | boolean;
 
function parseValue(value: InputValue): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else if (typeof value === "number") {
    return value.toFixed(2);
  } else {
    return value ? "TRUE" : "FALSE";
  }
}
 
 

API 多态响应

 
type ApiResponse = 
  | { status: "success"; data: User[] }
  | { status: "error"; code: number };
 
function handleResponse(response: ApiResponse) {
  if (response.status === "success") {
    console.log(response.data);  // ✅ 可访问 `data`
  } else {
    console.error(response.code); // ✅ 可访问 `code`
  }
}
 
 

交叉类型

将多个类型合并为一个类型,包含所有类型的成员。 ‌语法‌:Type1 & Type2 & ... & TypeN

interface Person { name: string }
interface Employee { id: number }
 
type Staff = Person & Employee; // { name: string; id: number }
 

核心特性‌

  • ‌类型范围‌:交叉类型是类型集合的 ‌逻辑与(AND)‌ 关系。
  • ‌成员合并‌:合并后的类型包含所有成员的并集。
 
type A = { x: number };
type B = { y: string };
type C = A & B;
 
const obj: C = { x: 1, y: "test" }; // ✅ 必须同时满足 A 和 B
 

应用场景

混入(Mixins)对象属性

 
type WithTimestamps<T> = T & {
  createdAt: Date;
  updatedAt: Date;
};
 
function addTimestamps<T>(obj: T): WithTimestamps<T> {
  return {
    ...obj,
    createdAt: new Date(),
    updatedAt: new Date(),
  };
}
 
const user = addTimestamps({ name: "Alice" });
console.log(user.createdAt); // ✅ 可访问合并后的属性
 
 
 

组合多个接口约束

 
interface Loggable { log(): void }
interface Serializable { serialize(): string }
 
type BusinessObject = Loggable & Serializable;
 
class Product implements BusinessObject {
  log() { console.log("Logging..."); }
  serialize() { return JSON.stringify(this); }
}
 

生产中的应用

构建通用存储库接口

  • 泛型接口 Repository<T> 抽象数据访问层,支持多种实体类型‌。
  • 通过具体类型参数 User 实现类型安全的 CRUD 操作‌。
 
// 泛型存储库接口
interface Repository<T> {
  get(id: string): Promise<T>;
  save(entity: T): Promise<void>;
}
 
// 用户存储库实现
class UserRepository implements Repository<User> {
  async get(id: string) {
    // 模拟数据库查询
    return { id, name: "Alice" };
  }
 
  async save(user: User) {
    console.log("Saving user:", user);
  }
}
 
 

React 中的接口使用

在 React 中,interface 常用于定义组件的 props 和 state,确保类型安全。

 
import React from 'react';
 
interface Props {
  name: string;
  age: number;
}
 
// React.FC<Props> 是 TypeScript 中的一个类型,表示一个 React 函数组件,它接受一个 props 参数,类型为 Props。
const UserProfile: React.FC<Props> = ({ name, age }) => {
  return (
    <div>
      <h1>{name}</h1>
      <p>{age}</p>
    </div>
  );
};
 
export default UserProfile;
 

后端开发中的接口使用

 
import express from 'express';
import { Request, Response } from 'express';
 
//  定义扩展的 UserRequest 接口。通过扩展 Request,UserRequest 继承了 Request 的所有属性和方法,并添加了一个新的属性 user。
interface UserRequest extends Request {
  user: {
    id: number;
    name: string;
  };
}
 
// 定义了一个路由处理器函数 getUser,它接受两个参数:
const getUser = (req: UserRequest, res: Response) => {
  res.json(req.user);
};
 
const app = express();
 
// 中间件,用于模拟用户信息
app.use((req: UserRequest, res: Response, next) => {
  req.user = {
    id: 1,
    name: "Alice"
  };
  next();
});
 
// 定义路由
app.get('/user', getUser);
 
// 启动服务器
app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
 
← Previous postVue夯实基础
Next post →Python中的yield