问题
The problem is that I want to use enum as key for a dictionary but I want to dynamically generate the entries using a loop. But I will prefer not to use ?
operator because I know I'll fill all keys and I don't want to force !
evaluation for each call.
enum Students {
A,
B,
C,
// many many students
}
// class Info(); // some properties like grades and teacher
const studentInfo: { [key in Students]: Info }; // won't work const must be initialized
const studentInfo: { [key in Students]: Info } = {}; // won't work as all keys are not defined
const studentInfo: { [key in Students]?: Info } = {}; // I want to avoid ?
for (let name of Students) {
// get info from some db
{ grade, teacher } = lookUp(name)
// initialize dictionary keys
studentInfo[name] = Info(grade, teacher);
}
studentInfo.A!.name; // I want to avoid !
Is there anyway to use the key itself to generate the dictionary so that the type signatures checkout. Maybe something like const studentInfo = { key for key in Students | .... };
I have checked out other answers which have led me to this somewhat specific questions. I'm still new to TypeScript, so still figuring out the rope but I'm really liking it.
回答1:
You're trying to define runtime behavior (i.e. the creation of an object) based on a construct that only exists at compile time (i.e. an enum
).
At compile time, your [key in Students]
type expression evaluates to [0, 1, 2]
, and not ["A", "B", "C"]
as you (seem to) assume.
At runtime however, this is what your TypeScript enum currently transpiles to in JavaScript:
var Students;
(function (Students) {
Students[Students["A"] = 0] = "A";
Students[Students["B"] = 1] = "B";
Students[Students["C"] = 2] = "C";
})(Students || (Students = {}));
If you would console.log(Students)
, you'll get the following output:
{
"0": "A",
"1": "B",
"2": "C",
"A": 0,
"B": 1,
"C": 2
}
As you can see, it's an object, not an array, so your for (let name of Students) {}
will not work (actually, it won't even compile). On the other hand, for (let name in Students) {}
will work, but will yield the 6 keys listed above instead of your 3 enum constants.
That's the runtime value that you would have to base your object creation on, so to get the behavior that you want, you'd need to filter out the non-numeric keys and create an object using the remaining keys. Obviously, the TypeScript compiler will not be able to infer the type of that object, so you would still have to specify the type that you've created yourself.
const studentInfo: { [key in Students]: Info } =
Object.values(Students)
.filter((v) => typeof v === 'number')
.reduce((a, v) => ({...a, [v]: new Info()}), {})
as { [key in Students]: Info };
Then to access the value in the studentInfo
object, the correct syntax would be:
studentInfo[Students.A].name;
Obviously, due to the fact that you're using enums far beyond their intended purpose, all of this feels very hacky.
To summarize:
- Don't base runtime behavior on compile-time constructs
- You probably shouldn't be using an
enum
here
来源:https://stackoverflow.com/questions/65434009/generate-typed-dictionary-with-a-for-loop-using-an-enum-as-key-type-but-without