"readonly" 在 TypeScript 中的用法

Usages of "readonly" in TypeScript

用法 1:函数声明其参数不会被修改

这种用法非常简单:作为合约,函数doSomething声明它不会改变接收到的参数。

interface Counter {
  name: string
  value: number
}

function doSomething(c: Readonly<Counter>) {
  // ...
}

let c = {
  name: "abc",
  value: 123
}
doSomething(c)
// Here we are sure that 'c.name' is "abc" and 'c.value' is '123'

用法 2:工厂声明其输出不可修改

使用此代码:

interface Counter {
  readonly name: string
  readonly value: number
  inc(): number
}

function counterFactory(name: string): Counter {
  let value = 0
  return {
    get name() {
      return name
    },
    get value() {
      return value
    },
    inc() {
      return ++value
    }
  }
}

我们这里有一个成员 readonly value 不能直接从外部修改。但是成员 inc() 可以改变值。此外,成员 value 声明为 readonly 但其值正在更改。

我想知道在成员 value 上使用 readonly 是否是继续进行的好方法。语法没问题。但是这个例子在 TypeScript 中语义正确吗?这就是修饰符 readonly 的用途吗?

属性 上的 readonly 关键字不能确保 属性 值是常量。没有与之对应的 TypeScript。使用只读 属性 我们唯一可以确定的是:

  1. 它的值不能从消费者端改变→用法1.
  2. 可以是get-only的推断类型 属性 → 用法2.注意,如果没有定义counterFactory return类型,它的推断与 Counter 接口完全一样,请参见下面代码中的 (A)。
  3. 它的值只能在对象构造期间设置一次→见下面的(B)。

代码示例:

// (A) Usage 2 using type inference
const counter = counterFactory('foo');
type Counter = typeof counter; // Produce the exact same type as the previous `Counter` interface
counter.value = 10; // [Ts Error] Can not assign to 'value' because it is a constant or read-only property

// (B) Readonly property initialization
// (B1) With an object literal + interface 
const manualCounter: Counter = { name: 'bar', value: 2, inc: () => 0 };
manualCounter.value = 3; // [Ts Error] Can not assign...

// (B2) With a class
class Foo {
  constructor(public name: string, public readonly value: number) { }
  inc() {
    return ++this.value; // [Ts Error] Can not assign...
  }
}

const foo = new Foo('bar', 3);
foo.value = 4; // [Ts Error] Can not assign...

// (C) Circumvent TypeScript
Object.assign(foo, { value: 4 }); // → No error neither in Ts, nor at runtime

这真的很令人困惑,因为它几乎就像一个常数 属性!用法2和案例C证明不是。

导致混淆的原因是 "double" 在您的工厂函数中使用了“值”这个词。

为清楚起见,重写为(请注意_value变量前的_):

interface Counter {
    readonly name: string
    readonly value: number
    inc(): number
}

function counterFactory(name: string): Counter {
    let _value = 0
    return {
        get name() {
            return name
        },
        get value() {
            return _value
        },
        inc() {
            return ++_value
        }
    }
}
  1. _value 只是一个局部变量,你不能改变
  2. get value() 实现接口定义 readonly value: number: counter.value is readonly