我可以在 TypeScript 中创建一个对于每个键值对都是类型安全的键值字典吗?

dop*_*ner 4 dictionary types typescript

这个问题有点复杂,最好的解决方法可能是探索一个基本的国家体系,所以请跟我一起走一分钟。假设我有这个状态类:

\n
class AccountState {\n  public id: string;\n  public displayName: string;\n  public score: number;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

jcalz\'s 的工作中,我知道我可以构建一个以类型安全的方式引用任何 AccountState 属性的函数\xe2\x80\x94我可以获取属性名称和值,并对其施加属性自己的类型限制使用泛型的价值,这是相当令人印象深刻的:

\n
class Store {\n  state = new AccountState();\n\n  mutate<K extends keyof AccountState>(property: K, value: AccountState[K]): void {\n    this.state[property] = value;\n  }\n}\n\nconst store = new Store();\nstore.mutate(\'displayName\', \'Joseph Joestar\'); // ok\nstore.mutate(\'displayName\', 5); // not ok: surfaces the below typescript error\n// ts(2345) Argument of type \'number\' is not assignable to parameter of type \'string\'.\n
Run Code Online (Sandbox Code Playgroud)\n

使用ValueOf<T>jcalz 的答案,我还可以建模一个类型安全的键值字典。对我来说,最简单的方法可能是向您展示它的工作原理以及它的缺点:

\n
type ValueOf<T> = T[keyof T];\n\nclass Store {\n  state = new AccountState();\n\n  mutateMany(updates: { [key in keyof AccountState]?: ValueOf<AccountState> }): void {\n    Object.keys(updates).forEach(property => {\n      const value = updates[property];\n      (this.state[property] as any) = value;\n    });\n  }\n}\n\nconst store = new Store();\nstore.mutateMany({ displayName: \'Joseph Joestar\', score: 5 }); // ok\nstore.mutateMany({ displayName: 1000, score: \'oh no\' }); // unfortunately, also ok\nstore.mutateMany({ score: true }); // not ok, surfaces the below error\n// ts(2322) Type \'boolean\' is not assignable to type \'ValueOf<AccountState>\'.\n// (if AccountState had a boolean property, this would be allowed)\n
Run Code Online (Sandbox Code Playgroud)\n

那第二个mutateMany()是一个问题。正如您所看到的,我可以要求密钥是 AccountState 的某些属性。我还可以要求该值对应于 AccountState 上的某些属性,因此它必须是string | number. 但是,不要求该值与属性的实际类型相对应。

\n

如何使字典完全类型安全,以便{ displayName: \'a\', score: 1 }允许使用eg,但{ displayName: 2, score: \'b\' }不允许使用eg?

\n

我考虑过声明一个 AccountStateProperties 接口,该接口简单地重复所有这些属性及其值,然后定义mutateMany(updates: AccountStateProperties),但这将为更多涉及的状态对象增加大量代码重复。直到今天我才知道我可以做其中一些事情,我想知道打字系统是否有一些我可以在这里利用的东西来使这本字典完全类型安全,而无需这种方法。

\n

wil*_*lis 6

mutateMany方法中[key in keyof AccountState]?: ValueOf<AccountState>,您是说对于 any key,值的类型可以是AccountState具有的任何类型。AccountState如果您尝试使用不在(例如)中的内容进行更新,您可以看到这一点true

相反,我相信你想要:

mutateMany(updates: { [key in keyof AccountState]?: AccountState[key] })
Run Code Online (Sandbox Code Playgroud)

这表示 at 的值还应该与atkey的类型匹配,而不仅仅是 的值的任何类型。AccountStatekeyAccountState

[编辑:如果您查看链接的答案,则以“为了确保键/值对在函数中正确“匹配”,您应该使用泛型以及查找类型......”开头的部分描述了这一点]