Generate typed dictionary with a for loop using an enum as key type but without using `?` to mark undefined in TypeScript

烂漫一生 提交于 2021-02-11 04:35:01

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!