概述

接口(Interface)和类型别名(Type Alias)是 TypeScript 中定义对象形状和类型的重要工具。它们帮助我们编写更安全、更可维护的代码。本教程将详细介绍接口和类型的使用方法。

接口基础

接口定义

// 基本接口定义
interface User {
  id: number;
  name: string;
  email: string;
  age?: number; // 可选属性
}

// 使用接口
const user: User = {
  id: 1,
  name: '张三',
  email: 'zhangsan@example.com',
  age: 25
};

// 可选属性可以省略
const user2: User = {
  id: 2,
  name: '李四',
  email: 'lisi@example.com'
};

只读属性

interface Product {
  readonly id: number;
  name: string;
  price: number;
  readonly createdAt: Date;
}

const product: Product = {
  id: 1,
  name: '商品A',
  price: 99.99,
  createdAt: new Date()
};

// product.id = 2; // 错误:不能修改只读属性
product.price = 89.99; // 正确:可以修改非只读属性

函数类型接口

// 定义函数类型接口
interface SearchFunc {
  (source: string, subString: string): boolean;
}

// 使用函数类型接口
const mySearch: SearchFunc = function(source: string, subString: string): boolean {
  const result = source.search(subString);
  return result > -1;
};

// 箭头函数形式
const mySearch2: SearchFunc = (source, subString) => {
  return source.includes(subString);
};

可索引类型

// 字符串索引签名
interface StringArray {
  [index: number]: string;
}

const myArray: StringArray = ['Bob', 'Fred'];
const myStr: string = myArray[0];

// 对象索引签名
interface StringDictionary {
  [key: string]: string;
}

const dictionary: StringDictionary = {
  name: '张三',
  email: 'zhangsan@example.com'
};

// 混合索引签名
interface Dictionary {
  [index: string]: string | number;
  length: number;
  name: string;
}

const dict: Dictionary = {
  length: 10,
  name: '字典',
  key1: 'value1',
  key2: 100
};

类类型接口

// 定义类接口
interface ClockInterface {
  currentTime: Date;
  setTime(d: Date): void;
}

// 实现接口的类
class Clock implements ClockInterface {
  currentTime: Date = new Date();
  
  setTime(d: Date) {
    this.currentTime = d;
  }
  
  getTime(): Date {
    return this.currentTime;
  }
}

const clock = new Clock();
clock.setTime(new Date('2025-01-01'));
console.log(clock.getTime());

接口继承

// 基础接口
interface Shape {
  color: string;
}

// 继承接口
interface Square extends Shape {
  sideLength: number;
}

interface Rectangle extends Shape {
  width: number;
  height: number;
}

// 多重继承
interface ColoredSquare extends Square, Rectangle {
  opacity: number;
}

const square: Square = {
  color: 'red',
  sideLength: 10
};

const coloredSquare: ColoredSquare = {
  color: 'blue',
  sideLength: 5,
  width: 5,
  height: 5,
  opacity: 0.8
};

混合类型接口

// 混合类型:对象 + 函数
interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  const counter = (function (start: number) {
    return `Count: ${start}`;
  }) as Counter;
  
  counter.interval = 123;
  counter.reset = function () {
    console.log('Reset counter');
  };
  
  return counter;
}

const c = getCounter();
console.log(c(10));
console.log(c.interval);
c.reset();

类型别名

基本类型别名

// 基本类型别名
type Name = string;
type Age = number;
type IsActive = boolean;

// 使用类型别名
const userName: Name = '张三';
const userAge: Age = 25;
const userActive: IsActive = true;

// 对象类型别名
type User = {
  id: number;
  name: string;
  email: string;
  age?: number;
};

const user: User = {
  id: 1,
  name: '李四',
  email: 'lisi@example.com'
};

联合类型

// 联合类型
type ID = number | string;

function printId(id: ID) {
  console.log(`ID: ${id}`);
}

printId(101);
printId('2025-01-01');

// 联合类型与类型守卫
type SuccessResponse = {
  status: 'success';
  data: any;
};

type ErrorResponse = {
  status: 'error';
  error: string;
};

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === 'success') {
    console.log('数据:', response.data);
  } else {
    console.log('错误:', response.error);
  }
}

handleResponse({ status: 'success', data: { name: '张三' } });
handleResponse({ status: 'error', error: '未找到资源' });

交叉类型

// 交叉类型
type Person = {
  name: string;
  age: number;
};

type Employee = {
  employeeId: number;
  department: string;
};

type EmployeePerson = Person & Employee;

const employee: EmployeePerson = {
  name: '王五',
  age: 30,
  employeeId: 1001,
  department: '技术部'
};

// 使用交叉类型扩展对象
type WithId = {
  id: number;
};

type Timestamped = {
  createdAt: Date;
  updatedAt: Date;
};

type Entity = WithId & Timestamped;

const entity: Entity = {
  id: 1,
  createdAt: new Date(),
  updatedAt: new Date()
};

泛型类型别名

// 泛型类型别名
type Container<T> = {
  value: T;
};

const numberContainer: Container<number> = {
  value: 42
};

const stringContainer: Container<string> = {
  value: 'Hello'
};

// 复杂的泛型类型别名
type Pair<T, U> = {
  first: T;
  second: U;
};

const pair: Pair<string, number> = {
  first: 'first',
  second: 2
};

// 条件类型别名
type NonNullable<T> = T extends null | undefined ? never : T;

type MaybeString = string | null;
type DefinitelyString = NonNullable<MaybeString>; // string

映射类型

// 映射类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

type User = {
  id: number;
  name: string;
  email: string;
};

// 只读用户
type ReadonlyUser = Readonly<User>;
const readonlyUser: ReadonlyUser = {
  id: 1,
  name: '张三',
  email: 'zhangsan@example.com'
};
// readonlyUser.name = '李四'; // 错误

// 部分用户
type PartialUser = Partial<User>;
const partialUser: PartialUser = {
  name: '李四'
};

// 必需类型
type Required<T> = {
  [P in keyof T]-?: T[P];
};

type OptionalUser = {
  id?: number;
  name?: string;
};

type RequiredUser = Required<OptionalUser>;
const requiredUser: RequiredUser = {
  id: 1,
  name: '张三'
};

接口 vs 类型别名

主要区别

// 1. 接口可以扩展,类型别名不能
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// 类型别名不能这样扩展
// type Cat extends Animal { // 错误
//   color: string;
// }

// 2. 类型别名可以用于联合类型和交叉类型
type ID = number | string;
type Person = { name: string } & { age: number };

// 接口不能表示联合类型
// interface ID = number | string; // 错误

// 3. 接口可以被类实现
interface Printable {
  print(): void;
}

class Document implements Printable {
  print() {
    console.log('打印文档');
  }
}

// 类型别名不能被类实现
// type Printable2 = { print(): void };
// class Document2 implements Printable2 { } // 错误

// 4. 类型别名可以用于原始类型
type Name = string;
type Age = number;

// 接口不能用于原始类型
// interface Name = string; // 错误

使用建议

// 使用接口的情况:
// 1. 定义对象的形状
interface User {
  id: number;
  name: string;
}

// 2. 需要扩展时
interface AdminUser extends User {
  permissions: string[];
}

// 3. 需要被类实现时
interface Serializable {
  serialize(): string;
}

class User implements Serializable {
  serialize() {
    return JSON.stringify(this);
  }
}

// 使用类型别名的情况:
// 1. 联合类型
type Status = 'pending' | 'success' | 'error';

// 2. 交叉类型
type UserWithTimestamp = User & {
  createdAt: Date;
  updatedAt: Date;
};

// 3. 泛型类型
type Result<T> = {
  data: T;
  error: null;
} | {
  data: null;
  error: Error;
};

// 4. 映射类型
type PartialUser = Partial<User>;

// 5. 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;

高级类型

类型守卫

// typeof 类型守卫
function printValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(`字符串: ${value.toUpperCase()}`);
  } else {
    console.log(`数字: ${value.toFixed(2)}`);
  }
}

// instanceof 类型守卫
class Bird {
  fly() {
    console.log('鸟在飞');
  }
}

class Fish {
  swim() {
    console.log('鱼在游');
  }
}

function move(animal: Bird | Fish) {
  if (animal instanceof Bird) {
    animal.fly();
  } else {
    animal.swim();
  }
}

// 自定义类型守卫
interface Bird {
  type: 'bird';
  flySpeed: number;
}

interface Fish {
  type: 'fish';
  swimSpeed: number;
}

function isBird(animal: Bird | Fish): animal is Bird {
  return animal.type === 'bird';
}

function moveAnimal(animal: Bird | Fish) {
  if (isBird(animal)) {
    console.log(`鸟以 ${animal.flySpeed} km/h 的速度飞行`);
  } else {
    console.log(`鱼以 ${animal.swimSpeed} km/h 的速度游泳`);
  }
}

可辨识联合

// 可辨识联合
interface Success {
  kind: 'success';
  value: string;
}

interface Error {
  kind: 'error';
  message: string;
}

interface Loading {
  kind: 'loading';
}

type Result = Success | Error | Loading;

function handleResult(result: Result) {
  switch (result.kind) {
    case 'success':
      console.log(`成功: ${result.value}`);
      break;
    case 'error':
      console.log(`错误: ${result.message}`);
      break;
    case 'loading':
      console.log('加载中...');
      break;
  }
}

handleResult({ kind: 'success', value: '数据加载成功' });
handleResult({ kind: 'error', message: '网络错误' });
handleResult({ kind: 'loading' });

条件类型

// 基本条件类型
type IsArray<T> = T extends any[] ? true : false;

type Test1 = IsArray<string>; // false
type Test2 = IsArray<number[]>; // true

// 条件类型中的类型推断
type Flatten<T> = T extends any[] ? T[number] : T;

type Str = Flatten<string[]>; // string
type Num = Flatten<number>; // number

// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;

type StrArrOrNumArr = ToArray<string | number>;
// string[] | number[]

// 条件类型约束
type MessageOf<T> = T extends { message: infer M } ? M : never;

interface Email {
  message: string;
}

interface Phone {
  phone: number;
}

type EmailMessage = MessageOf<Email>; // string
type PhoneMessage = MessageOf<Phone>; // never

映射修饰符

// 移除 readonly
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

interface ReadonlyUser {
  readonly id: number;
  readonly name: string;
}

type MutableUser = Mutable<ReadonlyUser>;
const mutableUser: MutableUser = {
  id: 1,
  name: '张三'
};
mutableUser.name = '李四'; // 正确

// 移除可选
type RequiredKeys<T> = {
  [P in keyof T]-?: T[P];
};

interface OptionalUser {
  id?: number;
  name?: string;
}

type RequiredUser = RequiredKeys<OptionalUser>;
const requiredUser: RequiredUser = {
  id: 1,
  name: '张三'
};

// 添加 readonly
type ReadonlyKeys<T> = {
  +readonly [P in keyof T]: T[P];
};

interface MutableUser {
  id: number;
  name: string;
}

type ReadonlyUser2 = ReadonlyKeys<MutableUser>;
const readonlyUser2: ReadonlyUser2 = {
  id: 1,
  name: '张三'
};
// readonlyUser2.name = '李四'; // 错误

实际应用示例

API 响应类型定义

// API 响应类型
interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: {
    code: number;
    message: string;
  };
}

// 用户类型
interface User {
  id: number;
  name: string;
  email: string;
  avatar?: string;
}

// 用户列表响应
type UserListResponse = ApiResponse<User[]>;

// 单个用户响应
type UserResponse = ApiResponse<User>;

// 使用示例
async function fetchUsers(): Promise<UserListResponse> {
  const response = await fetch('/api/users');
  const data: UserListResponse = await response.json();
  
  if (data.success && data.data) {
    console.log(`获取到 ${data.data.length} 个用户`);
  }
  
  return data;
}

async function fetchUser(id: number): Promise<UserResponse> {
  const response = await fetch(`/api/users/${id}`);
  const data: UserResponse = await response.json();
  
  if (data.success && data.data) {
    console.log(`用户: ${data.data.name}`);
  }
  
  return data;
}

表单类型定义

// 表单字段类型
interface FormField<T> {
  name: string;
  label: string;
  value: T;
  required: boolean;
  error?: string;
}

// 表单类型
interface Form<T extends Record<string, any>> {
  fields: {
    [K in keyof T]: FormField<T[K]>;
  };
  validate(): boolean;
  submit(): void;
}

// 用户注册表单
interface RegisterFormData {
  username: string;
  email: string;
  password: string;
  confirmPassword: string;
  age: number;
}

class RegisterForm implements Form<RegisterFormData> {
  fields: {
    [K in keyof RegisterFormData]: FormField<RegisterFormData[K]>;
  } = {
    username: {
      name: 'username',
      label: '用户名',
      value: '',
      required: true
    },
    email: {
      name: 'email',
      label: '邮箱',
      value: '',
      required: true
    },
    password: {
      name: 'password',
      label: '密码',
      value: '',
      required: true
    },
    confirmPassword: {
      name: 'confirmPassword',
      label: '确认密码',
      value: '',
      required: true
    },
    age: {
      name: 'age',
      label: '年龄',
      value: 0,
      required: false
    }
  };
  
  validate(): boolean {
    let isValid = true;
    
    // 验证用户名
    if (!this.fields.username.value) {
      this.fields.username.error = '用户名不能为空';
      isValid = false;
    }
    
    // 验证邮箱
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(this.fields.email.value)) {
      this.fields.email.error = '邮箱格式不正确';
      isValid = false;
    }
    
    // 验证密码
    if (this.fields.password.value.length < 6) {
      this.fields.password.error = '密码长度至少6位';
      isValid = false;
    }
    
    // 验证确认密码
    if (this.fields.password.value !== this.fields.confirmPassword.value) {
      this.fields.confirmPassword.error = '两次密码不一致';
      isValid = false;
    }
    
    return isValid;
  }
  
  submit(): void {
    if (this.validate()) {
      const formData: RegisterFormData = {
        username: this.fields.username.value,
        email: this.fields.email.value,
        password: this.fields.password.value,
        confirmPassword: this.fields.confirmPassword.value,
        age: this.fields.age.value
      };
      
      console.log('提交表单:', formData);
    }
  }
}

// 使用表单
const registerForm = new RegisterForm();
registerForm.fields.username.value = 'zhangsan';
registerForm.fields.email.value = 'zhangsan@example.com';
registerForm.fields.password.value = '123456';
registerForm.fields.confirmPassword.value = '123456';

if (registerForm.validate()) {
  registerForm.submit();
}

状态管理类型

// 状态类型
interface State<T> {
  data: T | null;
  loading: boolean;
  error: Error | null;
}

// Action 类型
type Action<T> = {
  type: 'FETCH_START';
} | {
  type: 'FETCH_SUCCESS';
  payload: T;
} | {
  type: 'FETCH_ERROR';
  error: Error;
};

// Reducer 类型
type Reducer<T> = (state: State<T>, action: Action<T>) => State<T>;

// 用户状态
type UserState = State<User>;

// 用户 Reducer
const userReducer: Reducer<User> = (state, action) => {
  switch (action.type) {
    case 'FETCH_START':
      return {
        ...state,
        loading: true,
        error: null
      };
    case 'FETCH_SUCCESS':
      return {
        ...state,
        loading: false,
        data: action.payload,
        error: null
      };
    case 'FETCH_ERROR':
      return {
        ...state,
        loading: false,
        error: action.error
      };
    default:
      return state;
  }
};

// 初始状态
const initialState: UserState = {
  data: null,
  loading: false,
  error: null
};

// 使用 Reducer
let currentState = initialState;

currentState = userReducer(currentState, { type: 'FETCH_START' });
console.log(currentState.loading); // true

currentState = userReducer(currentState, {
  type: 'FETCH_SUCCESS',
  payload: {
    id: 1,
    name: '张三',
    email: 'zhangsan@example.com'
  }
});
console.log(currentState.data); // User 对象

总结

本教程详细介绍了 TypeScript 接口和类型:

  1. 接口基础

    • 接口定义
    • 只读属性
    • 函数类型接口
    • 可索引类型
    • 类类型接口
    • 接口继承
    • 混合类型接口
  2. 类型别名

    • 基本类型别名
    • 联合类型
    • 交叉类型
    • 泛型类型别名
    • 映射类型
  3. 接口 vs 类型别名

    • 主要区别
    • 使用建议
  4. 高级类型

    • 类型守卫
    • 可辨识联合
    • 条件类型
    • 映射修饰符
  5. 实际应用

    • API 响应类型
    • 表单类型定义
    • 状态管理类型

接口和类型是 TypeScript 类型系统的核心,掌握它们对于编写类型安全的代码至关重要。

下一步

在下一教程中,我们将学习 TypeScript 函数和类,包括:

  • 函数类型

  • 类的定义和使用

  • 继承和多态

  • 访问修饰符

  • 抽象类和接口

  • 泛型类

继续学习 TypeScript,掌握这门强大语言的更多知识!