TypeScript 中的只读属性
Marius Schulz, “Read-Only Properties in TypeScript”, October 31, 2016
TypeScript 2.0 增加了 readonly 修饰符,用于标记只读属性。只读属性仅允许两种场景下的赋值,一种是在初始化的时候,另一种是在类的构造函数中,其余场景都不允许赋值。
我们来看个例子。有一个声明了两个只读属性 x 和 y 的简单类型 Point:
type Point = {
readonly x: number;
readonly y: number;
};
2
3
4
现在创建一个表示原点坐标的 origin 对象,x 和 y 的初始值都为 0:
const origin: Point = { x: 0, y: 0 };
因为 x 和 y 都被声明为只读的,所以它们是不能被修改的:
// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
origin.x = 100;
2
3
一个更实际的例子
上面的例子可能不太有说服力(确实),考虑下面的函数:
function moveX(p: Point, offset: number): Point {
p.x += offset;
return p;
}
2
3
4
moveX 函数不能修改给定坐标点的 x 属性,因为这个属性是只读的,否则 TypeScript 编译器会抱怨的:

相反,moveX 应该返回一个更新后的新坐标点,类似这样:
function moveX(p: Point, offset: number): Point {
return {
x: p.x + offset,
y: p.y
};
}
2
3
4
5
6
现在编译器就很开心了,因为我们没有修改只读属性,而是返回了一个新的坐标点:
只读类属性
我们还可以为类的属性应用 readonly 修饰符。下面的 Circle 类有一个只读属性 radius 和一个获取属性 area(也是只读的,因为没有 setter):
class Circle {
readonly radius: number;
constructor(radius: number) {
this.radius = radius;
}
get area() {
return Math.PI * this.radius ** 2;
}
}
2
3
4
5
6
7
8
9
10
11
注意,这里用到了 ES2016 新的指数操作符。radius 和 area 属性都能被外部访问(没有标记为 private),同时又不能被修改(都是只读的):
const unitCircle = new Circle(1);
unitCircle.radius; // 1
unitCircle.area; // 3.141592653589793
// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
unitCircle.radius = 42;
// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
unitCircle.area = 42;
2
3
4
5
6
7
8
9
10
11
只读索引签名
而且,索引签名也能用 readonly 修饰符标记成只读的,从而来避免索引属性被赋值。下面的 ReadonlyArray<T> 就是一个例子:
interface ReadonlyArray<T> {
readonly length: number;
// ...
readonly [n: number]: T;
}
2
3
4
5
因为索引是只读的,所以编译器认为下面的赋值是无效的:
const primesBelow10: ReadonlyArray<number> = [2, 3, 5, 7];
// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
primesBelow10[4] = 11;
2
3
4
5
readonly vs. 不可变
readonly 修饰符是 TypeScript 类型系统的一部分,只用来帮助编译器检查属性赋值是否合法。一旦编译成 JavaScript 代码后,readonly 标记会被移除。可以尝试 这个简单的例子 看下只读属性是如何被转译的。
因为 readonly 只是一个编译器设施,所以是无法为运行时提供保障的。也就是说,它是类型系统的另一个特性,通过让编译器检查 TypeScript 代码中意外的属性赋值,来帮助你写出正确的代码。
本篇文章是 TypeScript Evolution 系列中的一篇。