Skip to content
第 96 / 250 章前端⏱ 10 分钟阅读

第 96 章:对象类型与类型别名

学习目标

  • 掌握对象类型的多种定义方式
  • 学会用 type 给复杂类型起名
  • 理解类型别名的应用场景
  • 区分 typeinterface 的使用边界

一、对象类型的三种写法

1.1 对象字面量直接标注

typescript
function greet(user: { name: string; age: number }) {
  console.log(`Hello, ${user.name} (${user.age})`);
}

greet({ name: 'Tom', age: 18 });

适用场景:简单的、一次性的对象。

1.2 内联复杂结构

typescript
function createUser(
  data: { name: string; email: string; age?: number }
): { id: number; name: string; email: string } {
  return {
    id: Date.now(),
    name: data.name,
    email: data.email
  };
}

问题:参数和返回值都是对象字面量,重复且难以维护。

1.3 提取为类型(推荐)

typescript
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
}

function createUser(data: Omit<User, 'id'>): User {
  return {
    id: Date.now(),
    name: data.name,
    email: data.email
  };
}

二、type 关键字(类型别名)

2.1 基本语法

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

let u: User = { id: 1, name: 'Tom', email: 'tom@example.com' };

本质type 给一个类型起一个新名字,不创建新类型

2.2 别名 vs 原始类型

typescript
type ID = number;        // ID 就是 number 的别名
type Username = string;

let userId: ID = 123;
let name: Username = 'Tom';

// 完全等价
let a: ID = 456;
let b: number = 456;
a = b;  // ✅ 类型相同

2.3 别名 vs 联合/交叉

typescript
// 联合
type Status = 'pending' | 'success' | 'error';
type Result = string | number;

// 交叉
type Employee = { name: string } & { salary: number };
// 等价于 { name: string; salary: number }

2.4 给函数起别名

typescript
type GreetFunction = (name: string) => string;

const greet: GreetFunction = (name) => `Hello, ${name}!`;

// 函数签名复用
const welcome: GreetFunction = (name) => `Welcome, ${name}!`;

2.5 给数组/元组起别名

typescript
type StringArray = string[];
type Pair = [number, string];
type Matrix = number[][];

let arr: StringArray = ['a', 'b'];
let p: Pair = [1, 'one'];
let m: Matrix = [[1, 2], [3, 4]];

三、type vs interface 核心差异

3.1 共同点

typescript
// 都能定义对象
type UserT = { name: string };
interface UserI { name: string; }

// 都能定义联合(interface 需配合 type)
interface A { a: string }
interface B { b: number }
type AB = A | B;

3.2 差异一:能否定义联合/元组

typescript
// ✅ type 可以
type Status = 'pending' | 'success';
type Pair = [string, number];

// ❌ interface 不可以
// interface Status = 'pending' | 'success';  // ❌ 语法错误

3.3 差异二:能否自动合并

typescript
// interface 声明合并
interface User {
  name: string;
}
interface User {       // ✅ 自动合并
  age: number;
}
const u: User = { name: 'Tom', age: 18 };

// ❌ type 不可以重复声明
// type User = { name: string };
// type User = { age: number };  // ❌ Duplicate identifier 'User'

3.4 差异三:extends 与交叉

typescript
// interface 用 extends
interface Animal {
  name: string;
}
interface Dog extends Animal {
  breed: string;
}

// type 用交叉
type Animal = { name: string };
type Dog = Animal & { breed: string };

3.5 差异四:映射类型支持

typescript
// ❌ interface 不支持映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };

interface User {
  name: string;
  age: number;
}
type ReadonlyUser = Readonly<User>;  // ✅
// ❌ interface ReadonlyUser = Readonly<User>;

3.6 决策表

四、对象类型的高级特性

4.1 可选属性

typescript
type User = {
  name: string;
  email?: string;  // 可选
};

let u1: User = { name: 'Tom' };              // ✅
let u2: User = { name: 'Tom', email: 'a@b' }; // ✅

4.2 只读属性

typescript
type User = {
  readonly id: number;
  name: string;
};

const u: User = { id: 1, name: 'Tom' };
u.name = 'Jerry';      // ✅
u.id = 2;              // ❌ Cannot assign to 'id' because it is a read-only property

4.3 索引签名

typescript
type Dictionary = {
  [key: string]: string;
};

const dict: Dictionary = {
  name: 'Tom',
  email: 'tom@example.com'
};

4.4 嵌套对象

typescript
type Address = {
  street: string;
  city: string;
  zip: string;
};

type User = {
  name: string;
  address: Address;  // 嵌套
};

const u: User = {
  name: 'Tom',
  address: {
    street: '中关村大街 1 号',
    city: '北京',
    zip: '100000'
  }
};

4.5 数组字段

typescript
type Project = {
  name: string;
  members: string[];
  tags: { key: string; value: string }[];
};

const p: Project = {
  name: 'TaskFlow',
  members: ['Tom', 'Jerry'],
  tags: [
    { key: 'language', value: 'TypeScript' },
    { key: 'framework', value: 'Vue 3' }
  ]
};

五、类型别名的实战技巧

5.1 提取公共字段

typescript
// 基础类型
type BaseEntity = {
  id: number;
  createdAt: Date;
  updatedAt: Date;
};

// 扩展
type User = BaseEntity & {
  name: string;
  email: string;
};

type Product = BaseEntity & {
  title: string;
  price: number;
};

5.2 复用函数类型

typescript
type AsyncFunction<T> = (input: T) => Promise<T>;

const fetchUser: AsyncFunction<{ id: number }> = async ({ id }) => {
  // ...
  return { id };
};

5.3 类型映射的过渡

typescript
type User = {
  name: string;
  age: number;
  email: string;
};

// 把所有字段变成可选
type OptionalUser = {
  [K in keyof User]?: User[K];
};

type PartialUser = Partial<User>;  // TS 内置工具,效果相同

六、对象字面量的额外属性检查

typescript
type Config = {
  host: string;
  port: number;
};

function start(c: Config) { /* ... */ }

start({ host: 'localhost', port: 3000 });           // ✅
start({ host: 'localhost', port: 3000, debug: true }); // ❌ Object literal may only specify known properties

绕过方式

typescript
// 1. 用变量
const config = { host: 'localhost', port: 3000, debug: true };
start(config);  // ✅

// 2. 类型断言
start({ host: 'localhost', port: 3000, debug: true } as Config);  // ⚠️

// 3. 索引签名
type Config = {
  host: string;
  port: number;
  [key: string]: any;  // 允许任意额外字段
};

七、TypeScript 风格的 DTO

typescript
// DTO = Data Transfer Object(数据传输对象)
type UserDTO = {
  id: number;
  username: string;
  email: string;
  role: 'admin' | 'user';
  createdAt: string;  // ISO 字符串
};

type CreateUserDTO = Omit<UserDTO, 'id' | 'createdAt'>;
type UpdateUserDTO = Partial<Omit<UserDTO, 'id'>>;
type UserResponse = { code: number; data: UserDTO };

八、命名规范

typescript
// 类型/接口:PascalCase
type UserProfile = { ... };
interface OrderDetail { ... }

// 泛型参数:T、U、V 或 TKey、TValue
type Pair<T, U> = { first: T; second: U };

// 类型别名和值不要重名
type User = { ... };
// const User = ...;  // ❌ 容易混淆

九、常见错误

9.1 类型不匹配

typescript
type User = { name: string; age: number };

const u: User = { name: 'Tom', age: 'eighteen' };  // ❌ string not assignable to number

9.2 缺少字段

typescript
const u: User = { name: 'Tom' };  // ❌ Property 'age' is missing

9.3 多余字段(直接传字面量)

typescript
const u: User = { name: 'Tom', age: 18, extra: true };  // ❌

十、本章小结

要点关键
对象类型三种写法:内联、提取为 interface、提取为 type
type 关键字给类型起别名,可用于联合、元组、函数、数组
type vs interfacetype 适合联合/映射;interface 适合对象 + 声明合并
对象特性可选 ?、只读 readonly、索引签名、嵌套
DTO 模式Omit / Partial 派生新类型

动手练习

  1. 类型设计:为一个「博客系统」设计 UserPostComment 三个类型
  2. 类型派生:基于 User 类型派生 CreateUserDTO(无 id/createdAt)和 UpdateUserDTO(所有字段可选)
  3. type vs interface:把练习 1 的类型先用 interface 写一遍,再改成 type 写一遍,对比感受

推荐阅读


下一章第 97 章:函数类型

本站基于 VitePress 构建 · 由 Codebook 团队维护