对象类型
1. 对象类型定义
基本语法
-
匿名对象类型:直接在函数参数中声明。
tsfunction greet(person: { name: string; age: number }) { return "Hello " + person.name; }
-
接口(Interface):命名复用性强的对象类型。
tsinterface Person { name: string; age: number; } function greet(person: Person) { return "Hello " + person.name; }
-
类型别名(Type Alias):适用于更复杂的类型组合。
tstype Person = { name: string; age: number; }; function greet(person: Person) { return "Hello " + person.name; }
差异比较
- 接口 vs 类型别名 :
- 接口:支持合并(相同名称的接口会自动合并),适合定义契约。
- 类型别名 :一次性定义,不能重复声明,适合联合类型(
type ID = string | number
)。
2. 属性修饰符
可选属性
-
使用
?
标记属性为可选,调用时可省略。tsinterface PaintOptions { shape: Shape; xPos?: number; yPos?: number; } paintShape({ shape }); // 合法 paintShape({ shape, xPos: 100 }); // 合法
-
默认值处理:
-
使用解构赋值设置默认值,简化代码。
tsfunction paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) { console.log("x coordinate at", xPos); }
-
只读属性
-
使用
readonly
防止属性被修改。tsinterface SomeType { readonly prop: string; } const obj: SomeType = { prop: "value" }; obj.prop = "new value"; // ❌ 错误:不可写
-
嵌套对象的只读性:
-
readonly
只限制顶层属性,不影响内部对象的可变性。tsinterface Home { readonly resident: { name: string; age: number }; } let home: Home = { resident: { name: "Alice", age: 30 } }; home.resident.age++; // ✅ 允许修改内部属性 home.resident = { ... }; // ❌ 不允许重新赋值
-
3. 索引签名
动态键值对
-
描述未知属性名的对象。
tsinterface StringArray { [index: number]: string; } const arr: StringArray = ["a", "b"]; // ✅ console.log(arr[1]); // 输出 "b"
-
字符串索引签名:
-
限制所有属性值类型一致,适用于字典模式。
tsinterface Dictionary { [key: string]: number; } const dict: Dictionary = { a: 1, b: 2 }; // ✅
-
-
混合固定属性与索引签名:
-
固定属性类型必须是索引签名类型的子集。
tsinterface NumberOrStringDictionary { [key: string]: number | string; length: number; // ✅ 允许 name: string; // ✅ 允许 }
-
只读索引签名
-
防止通过索引修改值。
tsinterface ReadonlyStringArray { readonly [index: number]: string; } let arr: ReadonlyStringArray = ["a", "b"]; arr[0] = "c"; // ❌ 错误:不可修改
4. 超额属性检查
严格检查机制
-
创建对象字面量时,若包含目标类型未声明的属性,TypeScript 报错。
tsinterface SquareConfig { color?: string; width?: number; } createSquare({ colour: "red", width: 100 }); // ❌ 'colour' 不存在于 SquareConfig 中
绕过检查的方法
-
类型断言(需谨慎使用):
tscreateSquare({ colour: "red", width: 100 } as SquareConfig);
-
添加字符串索引签名:
tsinterface SquareConfig { [propName: string]: unknown; // 允许任意属性 color?: string; width?: number; }
-
中间变量赋值:
tsconst options = { colour: "red", width: 100 }; createSquare(options); // ✅ 无错误
5. 类型扩展与交叉
接口扩展
-
使用
extends
继承并扩展其他接口。tsinterface BasicAddress { street: string; city: string; } interface FullAddress extends BasicAddress { postalCode: string; }
交叉类型(Intersection Types)
-
使用
&
合并多个类型。tsinterface Colorful { color: string; } interface Circle { radius: number; } type ColorfulCircle = Colorful & Circle; const cc: ColorfulCircle = { color: "red", radius: 42, };
6. 工具类型
常用工具类型
-
Partial:将所有属性变为可选。
tstype PartialPerson = Partial<Person>; // name?: string; age?: number
-
Required:将所有属性变为必填。
tstype RequiredPerson = Required<Person>; // name: string; age: number
-
Readonly:将所有属性设为只读。
tstype ReadOnlyPerson = Readonly<Person>;
7. 实际应用场景
表单数据处理
-
动态表单字段类型化:
tsinterface FormData { [fieldName: string]: string | number | boolean | undefined; } function processForm(data: FormData) { Object.entries(data).forEach(([key, value]) => { if (typeof value === "string") { console.log(`${key}: ${value}`); } }); }
缓存实现
-
使用索引签名管理缓存数据:
tsinterface Cache<T> { [key: string]: { value: T; timestamp: number }; } class DataCache<T> { private cache: Cache<T> = {}; set(key: string, value: T): void { this.cache[key] = { value, timestamp: Date.now() }; } get(key: string): T | undefined { return this.cache[key]?.value; } }
8. 最佳实践
- 优先使用接口:定义明确的契约,便于扩展和维护。
- 合理使用可选属性:避免强制要求不必要的字段。
- 避免过度使用索引签名 :可能导致类型不安全,优先考虑
Map
或Record
。 - 谨慎使用类型断言:确保类型正确性,避免隐藏错误。
- 利用工具类型:减少重复代码,提升类型安全性。
9. 高级技巧
泛型对象类型
-
结合泛型实现灵活的类型约束:
tsinterface ApiResponse<T> { data: T; meta: { [key: string]: unknown }; } async function fetchData<T>(url: string): Promise<ApiResponse<T>> { const response = await fetch(url); return response.json(); }
映射类型
-
自定义映射类型转换属性:
tstype Optional<T> = { [K in keyof T]?: T[K]; }; type OptionalPerson = Optional<Person>; // name?: string; age?: number
10. 常见误区
-
索引签名与固定属性冲突:
-
固定属性类型必须符合索引签名类型,否则报错。
tsinterface User { [key: string]: string | number; // ❌ 下方的布尔值不符合 isAdmin: boolean; }
-
-
只读属性与不可变性的区别:
readonly
仅限制属性本身不可重写,不影响内部对象的修改。