- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
本篇技术指南以《The Concise TypeScript Book》韩文版章节 객체 타입(对象类型) 为核心骨架,系统讲解 TypeScript 中对象类型(Object Types)的定义方式、可选/只读属性、索引签名、类型扩展以及 interface 与 type 的选型差异。读完本文,你将掌握如何用最合适的方式描述对象形状,并在严格模式(strict)下写出类型安全的代码。
什么是对象类型
在 TypeScript 中,对象类型(Object Types)描述的是一个对象的“形状”(shape):它不仅指定了对象属性的名称和类型,还说明了这些属性是必选(required)还是可选(optional)。这一点与原文档的定义完全一致,也是理解整个 TypeScript 类型系统的基石。
// 一个描述用户的对象类型 interface User { name: string; age: number; email?: string; // 可选属性:email 可以存在,也可以不存在 }在 TypeScript 的术语体系中,“对象类型”指的是一个类型描述,而非具体的对象值。当编译后,这些类型注解会被擦除,运行时只剩纯 JavaScript 对象。
TypeScript 是**结构化类型(structural typing)**系统:两个类型只要形状兼容就互相匹配,并不要求显式的继承关系。这意味着我们定义的对象类型并不需要提前声明“我是谁的子类”,只要属性形状一致即可赋值。
定义对象类型的两种主要方式
TypeScript 中定义对象类型主要有两种方式:接口(interface)与类型别名(type alias)。原文档明确强调这是两种“主要方式(두 가지 방식)”,下面逐一展开。
方式一:使用 interface
接口通过指定属性的名称、类型和可选性来定义对象的形状:
interface User { name: string; age: number; email?: string; }name: string:必选属性,类型为string;age: number:必选属性,类型为number;email?: string:可选属性,通过属性名末尾的?标记,值为string或undefined。
除了普通属性,接口还可以声明方法。方法在语法上由“方法名 + 括号参数列表 + 返回类型”组成:
interface Person { name: string; age: number; greet(): void; // 方法:无参数,返回 void }这一通用语法在本书 인터페이스와 타입(接口与类型) 章节中有完整说明,每个属性声明之间用分号分隔。
方式二:使用 type 别名
类型别名在定义对象形状这一点上与接口几乎一致,但它更进一步:可以基于已有类型或已有类型的组合创建全新的自定义类型,包括联合类型(union)、交叉类型(intersection)以及其他复杂类型:
type Point = { x: number; y: number; };type 别名同样支持方法声明:
type Calculator = { add(a: number, b: number): number; subtract(a: number, b: number): number; };type 独有的能力在于组合:
type Department = 'dep-x' | 'dep-y'; // 联合类型(Union) type Employee = { id: number; department: Department; }; type EmployeeWithName = Employee & { name: string }; // 交叉类型(Intersection)联合与交叉类型的完整对比可参见本书 타입과 인터페이스의 차이점(type 与 interface 的差异) 章节。
匿名对象类型(Anonymous Object Types)
除了给类型命名,还可以**匿名地(anonymously)**定义对象类型——直接在变量注解或函数参数中内联写出对象形状。原文档给出了经典示例:
const sum = (x: { a: number; b: number }) => x.a + x.b; console.log(sum({ a: 5, b: 1 })); // 6这里的{ a: number; b: number }就是一个匿名对象类型,它只存在于参数注解中,没有名字。匿名类型的优点是不需要为一次性的小形状单独命名;缺点是无法复用,同样的形状如果出现在多处,需要重复书写。实际开发中,当某个对象形状在代码库中出现两次以上时,通常建议提取为interface或type。
匿名对象类型同样可以内联可选属性与只读属性:
const describe = (config: { name: string; verbose?: boolean; readonly id: number }) => { // ... };可选属性(Optional Properties)
在原文档的姊妹章节 선택적 프로퍼티(可选属性) 中,给出了可选属性的完整语法:在属性名末尾追加问号?。
type X = { a: number; b?: number; // Optional };可选属性需要注意两点:
- 可选属性的实际类型是
T | undefined——虽然可以直接访问,但 TypeScript 会提醒你它可能不存在; - 可选属性可以与**默认值(default value)**搭配使用,在解构时为缺失的属性提供回退值:
type X = { a: number; b?: number; }; const add = ({ a, b = 100 }: X) => a + b; console.log(add({ a: 1 })); // 101,b 缺失时使用默认值 100 console.log(add({ a: 1, b: 2 })); // 3只读属性(Readonly Properties)
使用readonly修饰符可以禁止对属性再次赋值。原文档的 읽기 전용 프로퍼티(只读属性) 章节强调了一个容易被忽视的细节:readonly 阻止重新写入属性,但并不保证深层不可变性(deep immutability)——它只是浅层保护。
interface Y { readonly a: number; } type X = { readonly a: number; }; // 使用内置工具类型 Readonly<T> 批量把属性变为只读 type J = Readonly<{ a: number; }>; // readonly 也可以用于索引签名 type K = { readonly [index: number]: string; };const obj: Y = { a: 1 }; // obj.a = 2; // ❌ 错误:无法赋值给 'a',因为它是一个只读属性readonly在interface、type与Readonly<T>工具类型中均可使用,且同样可以修饰索引签名(readonly [index: number]: string),适用于不允许修改的字典类型。
索引签名与类型索引(Index Signatures)
有时对象的键(key)无法预先枚举,例如字典(dictionary)结构。TypeScript 通过**索引签名(index signature)**解决这类问题。原文档在 인덱스 시그니처(索引签名) 与 타입 인덱싱(类型索引) 两章中有详细介绍。
// 类型索引:键为 string,值为泛型 T type Dictionary<T> = { [key: string]: T; }; const myDict: Dictionary<string> = { a: 'a', b: 'b' }; console.log(myDict['a']); // 'a'索引签名的键类型支持string、number与symbol三种:
type K = { [name: string | number]: string; }; const k: K = { x: 'x', 1: 'b' }; console.log(k['x']); // 'x' console.log(k[1]); // 'b' console.log(k['1']); // 'b',与 k[1] 结果相同这里有一个 JavaScript 层面的底层事实需要记住:JavaScript 会把number索引自动转换为string索引,因此k[1]与k["1"]访问的是同一个属性,返回值完全一致。理解了这一点,就不难解释为什么 TypeScript 允许数字键访问字符串索引的字典。
对象类型的扩展与组合
对象类型并非孤立存在,TypeScript 提供了丰富的扩展与组合手段。本书 타입 확장(类型扩展) 章节系统总结了四条规则:
1. interface 可以扩展 interface(单继承)
interface X { a: string; } interface Y extends X { b: string; }2. interface 可以扩展多个 interface
interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; }3. type 之间用交叉类型(&)组合
type A = { a: number }; type B = { b: number }; type C = A & B; // 同时拥有 a 与 b4. interface 可以扩展 type,反之不行
type A = { a: string; }; interface B extends A { b: string; // ✅ interface 可以 extends type } // ❌ type 无法 extends interface:type 的“继承”只能通过 & 交叉实现 // type C = B extends ... // 不存在这样的语法interface 与 type 的选型差异
什么时候用interface,什么时候用type?这是开发者最常问的问题之一。本书 타입과 인터페이스의 차이점(type 与 interface 的差异) 章节给出了权威对比,以下三条是最核心的差异:
差异一:声明合并(Declaration Merging)
interface支持声明合并:相同名字的多个接口会被 TypeScript 自动合并成一个,拥有合并后的全部属性与方法。type不支持声明合并。
interface A { x: string; } interface A { y: string; } // 编译器将两个 A 合并为一个 const j: A = { x: 'xx', y: 'yy', };声明合并在“不修改原有定义的情况下补充/修补第三方类型”时非常有用——例如为库中已有的接口追加自定义字段,无需改动库源码。
差异二:扩展语法不同
interface使用extends关键字继承其他接口的成员;type使用&交叉运算符组合多个类型。
// interface 扩展 interface A { x: string; y: number; } interface B extends A { z: string; } const car: B = { x: 'x', y: 123, z: 'z', }; // type 交叉 interface A2 { x: string; y: number; } type B2 = A2 & { j: string; }; const c: B2 = { x: 'x', y: 123, j: 'j', };差异三:对联合/交叉类型的表达能力
在定义联合与交叉类型时,type更加灵活:用|造联合,用&造交叉,非常直接。interface本身不能表示联合类型,但可以通过 type 间接使用(如type C = A | B,其中 A、B 都是 interface),而交叉类型则主要依赖&:
type Department = 'dep-x' | 'dep-y'; // Union type Person = { name: string; age: number; }; type Employee = { id: number; department: Department; }; type EmployeeInfo = Person & Employee; // Intersection // interface 参与联合 interface A { x: 'x'; } interface B { y: 'y'; } type C = A | B; // Union of interfaces实用选型建议
综合本书内容,可以给出如下实操准则:
| 场景 | 推荐 | 理由 |
|---|---|---|
| 描述对象/DTO/类契约 | interface | 支持声明合并,语义清晰,可扩展 |
| 需要联合类型、交叉类型、元组或基本类型组合 | type | 只有 type 能直接表达这些复杂组合 |
| 需要为第三方类型补丁 | interface | 声明合并能力 |
| 函数签名、工具类型、映射类型 | type | 与泛型、条件类型配合更自然 |
对象类型与运行时验证的结合
对象类型是编译期概念:类型检查在编译阶段完成,运行时并不会发生任何类型检查。因此当对象类型描述的数据来自外部(如 API 响应、用户输入、文件内容)时,需要配合运行时校验才能保证安全。本书 완전성 검사(穷尽性检查) 章节展示了一种基于never的类型级穷尽校验技巧,可作为对象联合类型处理的收尾防线:
type Direction = 'up' | 'down'; const move = (direction: Direction) => { switch (direction) { case 'up': console.log('Moving up'); break; case 'down': console.log('Moving down'); break; default: const exhaustiveCheck: never = direction; console.log(exhaustiveCheck); // 此行永远不会执行 } };never类型的意义在于:如果未来有人给Direction增加了第三个值(如'left'),而switch没有对应的case,那么direction在default分支中的类型就不再是never,const exhaustiveCheck: never = direction这一行会直接产生编译错误。这种“让编译器在漏处理时报警”的技巧,是保证对象联合类型演进安全性的最佳实践之一。
当对象类型的属性在条件分支中被细分判断时,则依赖本书 좁히기(类型收窄) 章节介绍的 typeof 守卫、真值收窄、in 运算符、instanceof 等手段——这些都是对对象类型进行更精细控制的核心配套知识。
在 strict 模式下使用对象类型
本仓库的书籍配套工具链在 tools/tsconfig.json 中开启了"strict": true以及"noImplicitAny": true。在严格模式下,对象类型的每个属性都必须有明确的类型注解,可选属性的处理也会更加严谨:
{ "compilerOptions": { "target": "es2022", "module": "commonjs", "strict": true, "noImplicitAny": true, "noUnusedLocals": false, "forceConsistentCasingInFileNames": true, "skipLibCheck": true, "lib": ["es2022", "esnext.disposable", "esnext.decorators", "dom"] } }在strict打开的前提下:
- 访问可选属性(
email?: string)时,需要先做空值判断或提供默认值,否则可能触发“可能为 undefined”的类型错误; - 为对象字面量赋值时,多余属性会触发过剩属性检查(excess property check),帮助尽早发现拼写错误或结构不匹配。
interface User { name: string; age: number; email?: string; } // ❌ strict 模式下会报错:对象字面量中的多余属性 'nickname' // const u: User = { name: 'a', age: 1, nickname: 'aa' }; // ✅ 可选属性访问前先做空值收窄 const getEmail = (user: User) => (user.email ? user.email.length : 0);总结
对象类型是 TypeScript 类型系统的核心构件。回顾原文档的三层主线,并结合本书配套章节,可以得出完整的能力图谱:
- 三种定义语法:
interface(适合描述对象形状与声明合并)、type(适合联合/交叉等复杂组合)、匿名对象类型(适合一次性内联形状); - 属性修饰能力:
?标记可选、readonly禁止重写、索引签名支持任意键的字典结构; - 扩展与组合:
extends用于接口继承,&用于类型交叉,并注意 interface 与 type 在声明合并、扩展语法上的差异; - 配套工程实践:结合
strict模式、never穷尽性检查与类型收窄,让对象类型在编译期就拦截尽可能多的错误。
掌握了对象类型,就掌握了描述数据形状的基本功——无论是领域模型、API 契约还是组件 props,都可以用类型系统清晰、安全地表达出来。
延伸阅读(同一书籍章节体系):
- 선택적 프로퍼티(可选属性)
- 읽기 전용 프로퍼티(只读属性)
- 인덱스 시그니처(索引签名)
- 타입 확장(类型扩展)
- 타입과 인터페이스의 차이점(type 与 interface 的差异)
- 英文原版章节 Object Types
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
TypeScript 对象类型(Object Types)完全指南:interface、type 别名与匿名类型的实战运用
TypeScript 对象类型(Object Types)完全指南:interface、type 别名与匿名类型的实战运用 对象类型(Object Types)
文档教程TypeScript 对象类型(Object Types)完全指南:interface、type 与匿名类型的实战解析
TypeScript 对象类型(Object Types)完全指南:interface、type 与匿名类型的实战解析 本文以《The Concise Type
文档教程uni-app UTS 对象类型(Object Types)完全指南:命名、实例化、嵌套限制与匿名对象
uni app UTS 对象类型(Object Types)完全指南:命名、实例化、嵌套限制与匿名对象 UTS(uni type script)是 uni ap
示例工程前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考