在 TypeScript 中,Readonly
是一个非常实用的内置工具类型,它用于将对象类型的所有属性变成只读,即这些属性在创建后不能被修改。Readonly
可以帮助我们保证数据的不可变性,这在很多场景下非常有用,特别是在管理状态、传递配置等情况下。
Readonly
Readonly
是 TypeScript 提供的一个工具类型,它会遍历对象类型中的每个属性,将这些属性标记为只读。这意味着一旦对象被创建后,无法再修改其属性的值。其实现原理和 Required
类似,都是通过 映射类型 来遍历每个属性,并使用 readonly
修饰符将属性变成只读。
Readonly
的语法如下:
Readonly<T>;
在上面的代码中 T
是表示传入的对象类型。Readonly<T>
是返回一个新类型,该类型将 T
中的所有属性都变为只读,确保这些属性不可修改。
假设我们有一个表示用户信息的接口,其中有一些属性:
interface User {
id: number;
name: string;
age: number;
}
const user: Readonly<User> = {
id: 1,
name: 'Alice',
age: 30,
};
// 错误:属性 'name' 是只读的,不能修改
user.name = 'Bob'; // 报错:无法分配到 "name" 因为它是只读属性
在上面的例子中,我们使用了 Readonly<User>
来将 User
类型的所有属性变成只读。然后我们尝试修改 name
属性时,TypeScript 会报错,告诉我们该属性是只读的,无法修改。
Readonly
同样可以应用于数组类型,将数组中的元素变成只读:
const arr: Readonly<number[]> = [1, 2, 3];
// 错误:不能修改只读数组元素
arr[0] = 4; // 报错:无法分配到 "0" 因为它是只读属性
// 错误:不能对只读数组调用 push
arr.push(4); // 报错:无法读取属性 'push',因为它是只读数组
在这个例子中,Readonly<number[]>
创建了一个只读的数字数组,数组元素和数组本身的结构都不可修改。
Readonly
与可变属性的关系
Readonly
只影响对象类型的属性,如果某个属性本身已经是只读的,它不会产生额外的影响。例如:
interface Point {
readonly x: number;
y: number;
}
const point: Readonly<Point> = { x: 10, y: 20 };
// 错误:'x' 是只读属性,不能修改
point.x = 15; // 报错:无法分配到 "x" 因为它是只读属性
// 错误:'y' 变成了只读属性
point.y = 25; // 报错:无法分配到 "y" 因为它是只读属性
在这个例子中,Readonly<Point>
会将 x
和 y
都变成只读的,尽管 x
原本就是只读的,但由于 Readonly
的作用,y
也变成了只读属性。
Readonly
的实现原理
Readonly
的核心实现是通过 映射类型(Mapped Types)来遍历对象类型的每个属性,并为其添加 readonly
修饰符。具体实现如下:
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
在上面的代码中:
-
[P in keyof T]
:遍历类型T
的所有属性。 -
readonly
:将每个属性标记为只读。 -
T[P]
:保留每个属性原本的类型。
9. 总结
Readonly
是 TypeScript 的工具类型,用于将对象的所有属性变为只读,确保它们不能被修改。它通过映射类型遍历属性并添加 readonly
修饰符,常用于确保数据不可变性,如配置对象或 API 返回的数据。