使用新类型作为键,使用for循环生成带类型的字典,但不使用`?`将TypeScript标记为未定义 [英] Generate typed dictionary with a for loop using a new type as key but without using `?` to mark undefined in TypeScript
问题描述
问题是我想使用新类型作为字典的键,但是我想使用循环来动态生成条目.但是我宁愿不使用?运算符,因为我知道我会填满所有键并且我不想强行使用!每个通话的评估.
The problem is that I want to use a new type 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.
const studentList = [
A,
B,
C,
// many many students
] as const;
type Students = typeof studentList[number];
// 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 !
无论如何,是否有使用密钥本身来生成字典的方法,以便类型签名签出.也许像 const studentInfo = {....};
.我认为应该解决的原因是因为编译器可以推断出该类型的所有键都已用于创建字典.
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 | .... };
. And the reason I feel this should work out is because the compiler can reason that all the keys of the type have been used to create the dictionary.
这有可能吗?
我以前用枚举来问这个问题的一个版本,但是由于这个答案,我意识到一种新的类型可能是更好,然后打这个新问题:).
I previously asked a version of this question with enums, but thanks to this answer I realized a new type might be better and hit this new question :).
推荐答案
这是我目前能想到的最好的方法:
This is the best I can come up with at the moment:
const students = ['A', 'B', 'C'] as const;
class Info {
name = '';
}
type StudentInfo = { [key in typeof students[number]]: Info; };
const studentInfo = students.reduce((a, v) => ({ ...a, [v]: new Info() }), {})
as StudentInfo;
studentInfo.A.name;
新的 Info()
部分是您在数据库中查找并创建实际的 Info
对象的地方.
The new Info()
part is where you would do your database lookup and create the actual Info
object.
理想情况下,在创建 studentInfo
对象时,也应该有一种避免这种转换的方法,但是这里的问题仍然是将运行时对象桥接到编译时结构.
Ideally, there should be a way to avoid that cast when creating the studentInfo
object as well, but again the problem here is bridging runtime objects to compile-time constructs.
正确定义了编译时间 StudentInfo
类型,但是TypeScript无法推断我的 reduce()
操作的结果与此类型相符.我以为使用ES2019 Object.fromEntries()
方法可能会更好,但是编译器仍然需要显式强制转换:
The compile time StudentInfo
type is correctly defined, but TypeScript is unable to infer that the result of my reduce()
operation conforms to that type. I thought I might have better luck using the ES2019 Object.fromEntries()
method, but the compiler still needs the explicit cast:
const studentInfo = Object.fromEntries(students.map(s => [s, new Info()]))
as StudentInfo;
更新
我问了一个新问题,看看是否有一种方法可以避免必须执行显式类型声明并获得一些好处答案.可以通过使用 Object.create(null)
而不是 {}
来初始化 reduce()
操作的结果来避免强制转换:
Update
I asked a new question to see if there was a way to get around having to do an explicit type assertion and got some good answers. The cast can be avoided by using Object.create(null)
instead of {}
to initalize the result of the reduce()
operation:
const studentInfo = students.reduce((a, v) => ({ ...a, [v]: new Info() }),
Object.create(null));
但是有一些警告.检查链接的问题以获取详细信息.
There are however some caveats. Check the linked question for details.
这篇关于使用新类型作为键,使用for循环生成带类型的字典,但不使用`?`将TypeScript标记为未定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!