Typescript 只读键的字典

3

在 TypeScript 中声明一个带有只读键的字典类是否可行?

declaration of dictionary class with readonly keys

代码:

class Dictionary {
    readonly[key: string]: string;

    constructor(props: Dictionary) {
        for (const key in props) {
            this[key] = props[key];
        }
    }
}

通常在构造函数中设置只读属性的值是允许的,但这里编译器报错:"类型“Dictionary”的索引签名仅允许读取。"


我们需要附上截图作为回答吗? - smnbbrv
2
如果您想为有效的答案添加屏幕截图,我将不胜感激,但我只添加了屏幕截图以显示Typescript编译器抱怨的位置。代码将作为文本添加,好的太晚了;)谢谢Nitzan。 - Kalle
2个回答

3
我不确定这是设计如此还是错误,文档对此描述很混乱:
一个属性或索引可以用 readonly 修饰符来声明并被视为只读。
但是:
只读属性可以有初始值并且在同一类声明的构造函数中可以被赋值,但是无法对只读属性进行赋值。
因此,虽然属性和索引签名都可以是只读的,但也许只有属性可以在构造函数中赋值。 不确定,请打开问题并找出答案。
但是,您可以使用Object.assign,它不会导致编译错误,并且还可以清理代码。
class Dictionary {
    readonly[key: string]: string;

    constructor(props: Dictionary) {
        Object.assign(this, props);
    }
}

let d1 = new Dictionary({ key1: "one", key2: "two" });
console.log(d1); // Dictionary {key1: "one", key2: "two"}

let d2 = new Dictionary(d1);
console.log(d1); // Dictionary {key1: "one", key2: "two"}

(在 playground 中查看代码)


编辑

使用 Object.assign 比使用 for/in 循环更好,这里有一个示例:

class A {
    [key: string]: any;

    constructor() {
        this["k1"] = "v1";
        this["k2"] = "v2";
    }

    method() {}
}

let a = new A();
let a1 = Object.assign({}, a);
console.log(a1); // { k1: "v1", k2: "v2" }

let a2 = {};
for (const key in a) {
    a2[key] = a[key];
}
console.log(a2); // { k1: "v1", k2: "v2", method: () }

如您所见,使用for/in循环还会迭代原型属性,这可能不是您想要的。
Object.assign就没有这个问题。 (在playground中查看代码)

另一个选项是使用Object.keys

let a3 = {};
Object.keys(a).forEach(key => a3[key] = a[key]);
console.log(a3); // { k1: "v1", k2: "v2" }

好的,Object.assign 现在看起来是一个有效的解决方案,但是我尽可能避免使用它,因为正如你在这里看到的,它只是覆盖了 readonly 标志。我会按照你的建议打开一个问题。 - Kalle
问题已经存在,请随时加入讨论 https://github.com/Microsoft/TypeScript/issues/6781 - Kalle
但是如果构造函数允许您分配值,那么它也会“覆盖只读标志”。请检查我的修订答案。 - Nitzan Tomer
这仅加强了我的感觉,即这确实是 TypeScript 编译器规范的问题。当然,Object.assign 也会迭代对象键,但当在构造函数中调用 Object.assign 时,它应该产生相同的编译器错误。 - Kalle
使用 Object.setProperty 并将它们设置为只读,这样在设置时实际上就像使用 get 关键字而不是 readonly(虽然必须在初始化时给出一个值,以防万一有人正在尝试使其工作)。请记住,readonly 只是一个提示,没有任何实际强制执行的方式... - Rycochet

0

Object.freeze(...); 防止在JS级别更改对象,并在Typescript中创建类型Readonly<T>。占位符... (T) 可以是原生字典、Map<X, Y>、数组或其他任何类型。因此,无需实现自己的字典,只需使用已提供的工具进行声明/创建即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接