简体   繁体   English

TypeScript:为什么我可以在构造函数中修改 `readonly` 属性?

[英]TypeScript: Why am I able to modify `readonly` property in constructor?

I have the following TypeScript snippet:我有以下打字稿片段:

class Jedi { 
  private readonly name: string = 'Skywalker';
  constructor(name: string) { 
    this.name = name; //Why I am able to modify name.
  }
  toString():string {
    return this.name;
  }
}

var jedi = new Jedi('Kenobi');

console.log(jedi.toString()); //Kenobi

As you can see in the code, I have declared name property as readonly .正如您在代码中看到的,我已将name属性声明为readonly As per my knowledge to declare properties as constant we use readonly in TypeScript .据我所知,将属性声明为常量,我们在 TypeScript 中使用readonly Once we declare it with initialization, we can not modify it.一旦我们用初始化声明它,我们就不能修改它。

But as you can see in the constructor it's being modified.但是正如您在constructor看到的那样,它正在被修改。 Also TypeScript compiler also does not warn about it.此外,TypeScript 编译器也不会对此发出警告。 Not sure whether it's a bug in TypeScript or it's intentional.不确定这是 TypeScript 中的错误还是有意为之。 Can anyone please explain?谁能解释一下?

The docs only say文档只说

Readonly properties must be initialized at their declaration or in the constructor.只读属性必须在其声明或构造函数中初始化。 ( Source ) 来源

But as Romain pointed out there is a little more info in the TypeScript 2.0 release notes:但正如 Romain 指出的,TypeScript 2.0 发行说明中有更多信息:

Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.只读属性可能有初始值设定项,并且可以在同一个类声明中的构造函数中赋值,但不允许对只读属性赋值。 ( Source ) 来源


But what confuses me is the compiled output.让我困惑的是编译后的输出。 See an example on the TS Playground .请参阅TS Playground上的示例。

As you can see the initially (readonly) property is overwritten without any warning from TS at compile time.正如您所看到的,初始(只读)属性在编译时被覆盖,而没有来自 TS 的任何警告。 I guess this is intentional, because the language behaves the same all the time and there are less edge cases.我猜这是故意的,因为语言的行为一直都一样,而且边缘情况更少。

EDIT: There is also a "sugarized" way to initialize a class with properties in TypeScript (I guess you know that but anyway):编辑:还有一种“糖化”的方式来用 TypeScript 中的属性初始化一个class (我想你知道但无论如何):

Adding public / private to arguments of the constructor signature will initialize those arguments as class properties.public / private添加到构造函数签名的参数会将这些参数初始化为类属性。 So in your example:所以在你的例子中:

class Jedi { 
    constructor(
        private readonly name: string = 'Skywalker'
    ) {}

    toString():string {
        return this.name;
    }
}

And if let TypeScript compile the above code, it actually works as expected (or at least what I would expect).如果让 TypeScript 编译上面的代码,它实际上会按预期工作(或者至少是我期望的那样)。 There is another Link to the Playground .还有另一个指向操场的链接

tl;dr; tl;博士; Initializing class properties inside the constructor signature via public / private will set the default value if none is given.如果没有给出,则通过public / private在构造函数签名中初始化类属性将设置默认值。 This does not work if you set class properties "by hand" ( this.name = name ).如果您“手动”设置类属性( this.name = name ),这将不起作用。 The later might still not be a bug but rather an intentional behaviour, because you explicitly set the class property.后者可能仍然不是错误而是有意的行为,因为您显式设置了类属性。

You can avoid this issue (or at least have TypeScript scream at you) by enabling noImplicitAny and strictNullChecks .你可以通过启用noImplicitAnystrictNullChecks来避免这个问题(或者至少让 TypeScript 向你尖叫)。 This way TypeScript will let you know that your class property might be undefined .这样 TypeScript 会让你知道你的类属性可能是undefined


What I would suggest is not using readonly property if you want to have immutability.如果您想拥有不变性,我建议不要使用readonly属性。 Not only is the above mentioned kinda weird behaviour, some less experienced developers might also think that a readonly array/object is really fully frozen/read only where it isn't.不仅上面提到的有点奇怪的行为,一些经验不足的开发人员还可能认为readonly数组/对象在它不是的地方真的完全冻结/只读。 Rather use something like ImmutableJS .而是使用类似ImmutableJS 的东西。

From the TypeScript documentation:从打字稿文档:

Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.只读属性可能有初始值设定项,并且可以在同一个类声明中的构造函数中赋值,但不允许对只读属性赋值。

More about the readonly keyword 关于readonly关键字的更多信息

Member variables that are declared as readonly can be assigned with their declaration, or from the constructor.声明为readonly成员变量可以通过它们的声明进行赋值,也可以从构造函数中赋值。 There's a certain C# familiarity with this.对此有一定的 C# 熟悉。

Example 1示例 1

Here, the readonly member variable is assigned directly, which is useful if you know what a value is, and that it's never going to change.这里,只读成员变量是直接赋值的,如果你知道一个值是什么并且它永远不会改变,这很有用。

class Foo {
    public readonly bar: number = 123;
}

Example 2示例 2

Here, the readonly member variable is declared, and then assigned from the constructor, which is useful if you don't yet know the final value, but some constructor logic will resolve this for you.这里声明了 readonly 成员变量,然后从构造函数赋值,如果你还不知道最终值,这很有用,但一些构造函数逻辑会为你解决这个问题。

class Foo {
    public readonly bar: number;

    constructor(bar: number) {
        this.bar = 123 * bar / 100;
    }
}

Example 3示例 3

Here the readonly member variable uses TypeScript's short-hand member variable declaration, where global member variables can be assigned from the constructor, which is useful if you want the user to input a value which will then never change.这里的 readonly 成员变量使用了 TypeScript 的简写成员变量声明,全局成员变量可以从构造函数中赋值,如果你希望用户输入一个永远不会改变的值,这很有用。 In this particular example, bar can either be set by the user, or will default to 123 .在此特定示例中, bar可以由用户设置,也可以默认为123

class Foo {
    constructor(public readonly: bar: number = 123) {
    }
}

Example 4示例 4

This is something you don't want to do, because the emitted JavaScript causes two assignments to the same variable.这是您不想做的事情,因为发出的 JavaScript 会导致对同一变量进行两次赋值。 I'm just adding this so that future developers know they probably shouldn't do this.我只是添加这个,以便未来的开发人员知道他们可能不应该这样做。

class Foo {
    public readonly bar: number = 123;

    constructor(bar: number) {
        this.bar = bar;
    }
}

The emitted JavaScript will look something like this...发出的 JavaScript 看起来像这样......

var Foo = (function () {
    function Foo(bar) {
        this.bar = 123; // :)
        this.bar = bar; // :(
    }

    return Foo;
})();

What about immutability?不变性呢?

readonly is only upheld by TypeScript and it's compiler; readonly仅由 TypeScript 及其编译器支持; there's no readonly in JavaScript (well, that's not entirely true, just that TypeScript doesn't emit truly readonly values) . JavaScript 中没有只读(嗯,这并不完全正确,只是 TypeScript 不会发出真正的只读值) There are a couple of workarounds for this (and yes, it would be nice if TypeScript could emit these for us!)对此有几种解决方法(是的,如果 TypeScript 可以为我们发出这些,那就太好了!)

Creating an immutable member variable创建不可变成员变量

class Foo {
    // TypeScript respects "readonly"
    public readonly bar: number;

    constructor(bar: number) {
        // JavaScript respects property that isn't writable.
        Object.defineProperty(this, "bar", {
            value: bar,
            enumerable: true,
            configurable: false,
            writable: false
        });
    }
}

Creating an immutable class创建一个不可变的类

class Foo {
    constructor(public readonly bar: number) {
        // Nothing in the object is allowed to be reassigned.
        Object.freeze(this);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Typescript为什么我能够动态修改TypeScript对象 - Typescript why am i able to to dynamically modify TypeScript object 为什么打字稿中的“只读”属性不起作用? - Why is the 'readonly' property in typescript not working? 如何更改 TypeScript 中的只读属性? - How can I change a readonly property in TypeScript? 为什么我不能在打字稿类的构造函数中访问抽象属性 - Why can't I access an abstract property in the constructor of a typescript class typescript readonly类的属性 - typescript readonly property of class 为什么打字稿允许按可变类型引用/分配只读类型/属性? - Why typescript allows referencing/assigning a readonly type/property by mutable type? 为什么我无法在Angle 5和Typescript中使文本区域部分不可编辑? - why i am not able to make the text area partially non editable in angular 5 and typescript? 打字稿:为什么我可以为声明的(接口)数字对象参数分配字符串值 - Typescript: Why am I able to allocate a string value to my declared (interface) number object parrameter 为什么我不能在TypeScript中以这种方式只读取数组? - Why can't I make an Array readonly this way in TypeScript? Typescript:在构造函数内部的函数中分配一个只读属性 - Typescript: assign a readonly attribute in a function inside the constructor
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM