【发布时间】:2021-05-10 02:18:30
【问题描述】:
这个问题有点牵强,最好通过探索一个基本的状态系统来解决,所以和我一起在这里走一分钟。假设我有这个状态类:
class AccountState {
public id: string;
public displayName: string;
public score: number;
}
从jcalz's work here,我知道我可以构建一个以类型安全的方式引用任何 AccountState 属性的函数——我可以获取属性名称和值,并使用泛型对该值施加属性自己的类型限制,这非常棒令人印象深刻:
class Store {
state = new AccountState();
mutate<K extends keyof AccountState>(property: K, value: AccountState[K]): void {
this.state[property] = value;
}
}
const store = new Store();
store.mutate('displayName', 'Joseph Joestar'); // ok
store.mutate('displayName', 5); // not ok: surfaces the below typescript error
// ts(2345) Argument of type 'number' is not assignable to parameter of type 'string'.
使用 jcalz 的回答中的ValueOf<T>,我还可以建模一个类型安全的键值字典。对我来说,向您展示它的工作原理以及它的缺点可能是最简单的:
type ValueOf<T> = T[keyof T];
class Store {
state = new AccountState();
mutateMany(updates: { [key in keyof AccountState]?: ValueOf<AccountState> }): void {
Object.keys(updates).forEach(property => {
const value = updates[property];
(this.state[property] as any) = value;
});
}
}
const store = new Store();
store.mutateMany({ displayName: 'Joseph Joestar', score: 5 }); // ok
store.mutateMany({ displayName: 1000, score: 'oh no' }); // unfortunately, also ok
store.mutateMany({ score: true }); // not ok, surfaces the below error
// ts(2322) Type 'boolean' is not assignable to type 'ValueOf<AccountState>'.
// (if AccountState had a boolean property, this would be allowed)
第二个mutateMany() 是个问题。如您所见,我可以要求密钥是 AccountState 的某些属性。我还可以要求该值对应于 AccountState 上的某些属性,因此它必须是 string | number。但是,并不要求该值与属性的实际类型相对应。
如何使字典完全类型安全,例如允许{ displayName: 'a', score: 1 },但不允许{ displayName: 2, score: 'b' }?
我考虑过声明一个 AccountStateProperties 接口,它简单地重复所有这些属性及其值,然后定义 mutateMany(updates: AccountStateProperties),但这会为更多涉及的状态对象添加大量代码重复。直到今天我才知道我可以做一些这些事情,我想知道打字系统是否有一些东西我可以在这里利用来使这本字典在没有这种方法的情况下完全类型安全。
【问题讨论】:
标签: typescript dictionary types