基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

readonly 修饰符的作用

知识点图片

TypeScript 中的 readonly 修饰符主要用于将属性标记为不可变(immutable)

一旦一个属性被标记为 readonly,它就只能在初始化时在构造函数(Constructor)中被赋值。之后尝试修改该属性的值,TypeScript 编译器会报错。

以下是 readonly 的主要应用场景和细节:

1. 在接口(Interface)和类型别名(Type Alias)中使用

这是最常见的用法,用于约束对象结构,防止意外修改属性。

typescript
interface Point {
    readonly x: number;
    readonly y: number;
}

let p1: Point = { x: 10, y: 20 };

// ✅ 读取是允许的
console.log(p1.x); 

// ❌ 报错:无法分配到 "x" ,因为它是只读属性。
// p1.x = 5; 

2. 在类(Class)中使用

在类中,readonly 属性只能在声明时构造函数里赋值。

typescript
class Animal {
    readonly name: string;

    constructor(theName: string) {
        // ✅ 在构造函数中赋值是允许的
        this.name = theName;
    }

    changeName() {
        // ❌ 报错:只能在构造函数中修改
        // this.name = "Bob"; 
    }
}

const cat = new Animal("Kitty");
// cat.name = "Tom"; // ❌ 报错

参数属性简写(Parameter Properties):
TypeScript 允许在构造函数参数中直接使用 readonly,这会自动创建同名的类属性并赋值,非常简洁。

typescript
class Person {
    // 等同于声明了属性并赋值
    constructor(readonly name: string) {
    }
}

const p = new Person("Alice");
console.log(p.name); // Alice
// p.name = "Bob"; // ❌ 报错

3. 只读数组(ReadonlyArray)

如果你希望一个数组被创建后不能被修改(不能 push, pop, splice 等),可以使用 ReadonlyArray<T>readonly T[]

typescript
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;

// ro[0] = 12; // ❌ 报错
// ro.push(5); // ❌ 报错
// ro.length = 100; // ❌ 报错

// ✅ 但你可以把它重新赋值给一个普通数组(需要类型断言)
a = ro as number[];

4. Readonly<T> 工具类型

TypeScript 提供了一个内置的工具类型 Readonly<T>,它可以将类型 T所有属性都变为只读。

typescript
interface Todo {
    title: string;
    description: string;
}

const myTodo: Readonly<Todo> = {
    title: "学习 TypeScript",
    description: "掌握 readonly"
};

// myTodo.title = "睡觉"; // ❌ 报错:所有属性现在都是只读的

5. 关键细节与注意事项

A. 它是“浅层”只读 (Shallow Readonly)

readonly 只能确保属性本身的引用不被修改。如果该属性是一个对象,对象内部的属性依然是可以修改的

typescript
interface Person {
    readonly info: {
        age: number;
    };
}

const p: Person = { info: { age: 18 } };

// p.info = { age: 20 }; // ❌ 报错:不能修改 info 的引用

p.info.age = 20; // ✅ 允许!info 对象内部的属性没有被标记为 readonly

注:如果需要深层只读,通常需要结合递归类型或使用第三方库(如 DeepReadonly)。

B. const vs readonly

  • const:用于变量。防止变量被重新赋值。
  • readonly:用于属性。防止对象的属性被修改。
typescript
const foo = { x: 1 }; 
foo.x = 2; // ✅ const 不限制对象内部属性的修改

interface Bar {
    readonly x: number;
}
const bar: Bar = { x: 1 };
// bar.x = 2; // ❌ readonly 限制属性修改

C. 编译时检查 vs 运行时行为

readonly 只是 TypeScript 的编译时(Compile-time) 检查。代码编译成 JavaScript 后,readonly 关键字会被移除。这意味着在运行时(Runtime),如果你强行绕过类型检查(例如通过 any),依然可以修改该值。

如果要实现运行时的绝对只读,需要使用 JavaScript 原生的 Object.freeze()

总结

使用 readonly 的主要目的是表达意图提高代码安全性。它告诉其他开发者(以及编译器):这个属性初始化之后就不应该再变了,从而避免副作用和意外的 Bug。

00:00
00:00