对象类型
本节目标:设计可读的对象契约,并理解可选、只读、索引签名、接口扩展和交叉类型。
属性修饰符
对象类型描述需要哪些属性以及各自的类型。TypeScript 采用结构化类型:只要对象拥有目标需要的成员,通常就能赋值给该类型。
interface Task {
readonly id: number;
title: string;
done?: boolean;
}
function taskLabel(task: Task): string {
return `${task.done ? "✓" : "○"} ${task.title}`;
}? 表示可选;readonly 限制通过该引用重新赋值。它们只控制静态检查,不冻结运行时对象,也不保证嵌套对象不可变。
额外属性检查与赋值兼容
把对象字面量直接传给函数时,检查器会额外检查拼写错误:
interface Options { color?: string }
function paint(options: Options) { /* ... */ }
paint({ color: "blue" });
// paint({ colour: "blue" }); // 额外属性检查会报告可能的拼写错误先赋值给变量后,结构兼容规则可能接受带有额外成员的对象。这不代表任意对象字面量都能传入;额外属性检查是帮助发现错误的特殊检查,不是精确对象类型系统。
索引签名
当对象键集合事先未知时,可用索引签名描述键和值:
interface Scores {
[student: string]: number;
}
const scores: Scores = { Lin: 95, Kai: 88 };索引签名表示任意字符串键都对应数字。若键集合有限,优先使用明确属性或 Record<Keys, Value>,这样拼写检查更严格。
interface 与 type
两者都能描述对象结构。interface 适合可扩展的对象契约;type 还可表达联合、交叉、条件等组合类型。选择应保持团队一致,没必要强行只用其中一种。
接口扩展:
interface Identified { id: number }
interface Article extends Identified { title: string }交叉类型把条件组合在一起:
type Timestamped = { createdAt: Date };
type SavedArticle = Article & Timestamped;接口扩展会创建明确的继承关系;交叉类型要求一个值同时满足多个类型。相同属性冲突时交叉结果可能变成难以使用的 never,应避免无意的冲突。
函数属性、数组与元组
对象可以有函数属性,也可以用方法语法描述调用能力。数组是带数字索引的对象;只读数组可接受普通数组,但不能通过该引用修改元素。
type ReadonlyPair = readonly [string, number];
const entry: ReadonlyPair = ["章节", 1];
function first<T>(items: readonly T[]): T | undefined {
return items[0];
}元组按位置描述固定长度或已知位置的元素类型;数组适合数量不固定、元素类型相同的集合。开启 noUncheckedIndexedAccess 可让一般数组索引访问显式包含 undefined,更准确反映越界可能性。
小结与练习
- 对象类型按结构兼容;额外属性检查主要帮助发现新鲜对象字面量中的拼写错误。
interface和type都可表达对象契约,后者也支持联合等类型运算。- 用只读数组表达“不通过此函数修改集合”的约束。
练习:为课程目录定义 Course、VideoCourse 与 BookCourse,共享部分字段,并分别约束视频时长和书籍页数。下一节:从类型创建类型,学习根据已有类型派生新类型。