Is it possible to declare a dictionary class in typescript with readonly keys?
Code:
class Dictionary {
readonly[key: string]: string;
constructor(props: Dictionary) {
for (const key in props) {
this[key] = props[key];
}
}
}
Normally setting the values of readonly properties is allowed in the constructor, but here the compiler complains that "Index signature of type 'Dictionary' only permits reading'.
I'm not sure if this is by design or not, the docs are confusing about this:
A property or index signature can now be declared with the readonly modifier is considered read-only.
But:
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
So while both properties and index signature can be readonly, maybe only properties can be assigned in the constructor.
Not sure about it, you can open an issue and find out.
You can however use Object.assign which wouldn't end up with a compilation error and it cleans out the code as well:
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"}
Using Object.assign
is better than your for/in loop, here's an example why:
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: () }
As you can see, using for/in loop will also iterate over prototype properties, which probably isn't what you want.
Object.assign
doesn't have this problem.
Another option is to use Object.keys:
let a3 = {};
Object.keys(a).forEach(key => a3[key] = a[key]);
console.log(a3); // { k1: "v1", k2: "v2" }