"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.
- 可以是get-only的推断类型 属性 → 用法2.注意,如果没有定义
counterFactory
return类型,它的推断与 Counter
接口完全一样,请参见下面代码中的 (A)。
- 它的值只能在对象构造期间设置一次→见下面的(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
}
}
}
_value
只是一个局部变量,你不能改变
get value()
实现接口定义 readonly value: number
: counter.value
is readonly
用法 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.
- 可以是get-only的推断类型 属性 → 用法2.注意,如果没有定义
counterFactory
return类型,它的推断与Counter
接口完全一样,请参见下面代码中的 (A)。 - 它的值只能在对象构造期间设置一次→见下面的(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
}
}
}
_value
只是一个局部变量,你不能改变get value()
实现接口定义readonly value: number
:counter.value
is readonly