TypeScript接口中getter与readonly属性的区别是什么?
TypeScript中readonly属性接口与getter接口的差异
先看这两个TypeScript接口定义:
interface A { readonly x: number; } interface B { get x(): number; }
有人发现,实现这两个接口时,方式可以互换:用getter实现接口A,或者用readonly属性实现接口B,代码示例如下:
class ClassA implements A { // x = 1; <- 这样写也能实现A get x() { return 1; } } class ClassB implements B { // get x() { <- 这样写也能实现B // return 1; // } x = 1; }
那这两个接口之间到底有没有差异?答案是有明显差异,具体体现在以下几个方面:
1. 字面量赋值的限制不同
- 对于接口
A,可以直接给变量赋值普通字面量,因为readonly x只是编译时的只读约束,允许初始化赋值:const a: A = { x: 1 }; // 完全合法 - 对于接口
B,不能直接赋值普通字面量,因为接口要求的是get x()访问器,普通字面量的属性不符合这个要求:const b: B = { x: 1 }; // 编译报错:类型"{ x: number; }"无法赋值给类型"B" // 正确写法必须带getter: const b: B = { get x() { return 1; } };
2. 运行时行为的本质区别
- 接口
A的readonly x是编译时约束,编译后的JS代码中,这个属性和普通属性没有区别,只要绕开TypeScript的类型检查,运行时可以修改:const a: A = { x: 1 }; (a as any).x = 2; // 运行时能成功修改,不会报错 - 接口
B的get x()是真正的访问器,编译后的JS代码里会生成getter函数,运行时尝试赋值会被忽略(严格模式下直接报错):const b: B = { get x() { return 1; } }; (b as any).x = 2; // 非严格模式下无报错,但x的值还是1;严格模式下抛错
3. 接口扩展/合并的规则不同
- 扩展
readonly接口时,无法将readonly属性改为可写:interface ExtendA extends A { x: number; // 编译报错:无法将只读属性"x"修改为可写 } - 扩展getter接口时,可以添加对应的setter,把只读访问器改成可读可写:
interface ExtendB extends B { set x(value: number); // 完全合法,此时ExtendB的x是可读可写的 }
4. 类型兼容性的细微差异
虽然类实现时可以互换方式,但在某些场景下,两个接口的类型兼容性并不完全对称:
- 带有
readonly属性的类型可以赋值给getter接口类型:const a: A = { x: 1 }; const b: B = a; // 合法 - 但反过来,如果getter接口的实现是动态计算值的对象,赋值给readonly接口类型虽然合法,但运行时行为不同(每次访问x都会调用getter),这可能和预期的readonly属性行为(一次赋值后不变)有差异。
内容的提问来源于stack exchange,提问作者kzuraniewski
相关产品推荐
相关产品推荐

