typescript - Typescript 泛型不会像我希望的那样解析具体类型
问题描述
我有一个保存项目列表的类,但不是将它们保存在一个平面数组中,而是将它们保存在一个对象映射中,其中每个属性代表一组项目。就像我们有一个汽车清单,我们会按制造商对它们进行分组。
// object map
interface IObjectMap<TValue> {
[key: string]: TValue;
}
// object map; same as interface but build using keys union type
type ItemMap<TMapKeys extends keyof IObjectMap<unknown>, TValue> = Record<TMapKeys, TValue[]>;
// just a function taking one parameter and returning a result
type GetterFunc<TInput, TResult> = (item: TInput) => TResult;
// object map where the value type of properties are getter functions returning a string
type GetterMap<TMapKeys extends keyof IObjectMap<unknown>, TInput> = Record<TMapKeys, GetterFunc<TInput, string>>;
class GroupedItems<
TItem, // item type
TGroupKeys extends keyof IObjectMap<unknown> // object map keys
> {
public groups: ItemMap<TGroupKeys, TItem> = {} as ItemMap<TGroupKeys, TItem>;
public countryGetters: GetterMap<TGroupKeys, TItem> = {} as GetterMap<TGroupKeys, TItem>;
public addItems(items: TItem[], getGroupKey: GetterFunc<TItem, TGroupKeys>): void {
this.items.concat(items);
this.items
.forEach(item => {
let name = getGroupKey(item);
if (this.groups[name] === undefined) {
// create placeholder for items
this.groups[name] = [];
}
// Put the item in the group
this.groups[name].push(item);
});
}
public assignGetters(getters: GetterMap<TGroupKeys, TItem>) {
this.countryGetters= getters;
}
}
该类有两个泛型类型参数:
- 我们将分组的项目类型(即
Car
) - 组键(即
'renault' | 'peugeot' | ...
)
然后类成员都被定义为对象映射:
groups
将项目保留在组数组中countryGetters
也是一个与项目组具有相同属性的对象映射,但它们定义了一个函数,该函数返回传入的汽车的制造商国家
使用示例
上面的代码似乎没有错误,但随着它的使用,类型似乎没有得到应有的解析。当我尝试使用未在映射或联合类型的组键中定义的项目组名称时,我希望编译器/linter 抱怨...
interface Car {
model: string;
year: number;
}
interface CarMakers<TValue> extends IObjectMap<TValue> {
renault: TValue;
peugeot: TValue;
}
let select = new GroupedItems<
Car,
keyof CarMakers<unknown>
>();
select.addItems([
{ model: 'R5', year: 1980, dummy: false }, // error; correct
{ model: '206', year: 2004 },
{ model: '3008', year: 2010 }
],
car =>
car.year < 2000
? 'audi' // should be an error; "audi" not in "keyof MakerGroups<>"
: 'peugeot'
);
select.assignGetters({
renault: () => 'France',
audi: () => 'Germany' // should be an error; "audi" not in "keyof MakerGroups<>"
});
如您所见,Typescript 没有解析组名,所以我的类型定义不够严格(我想),因此我可以无效地操作不存在的组。从编译时的角度来看,上面的代码似乎很好,但应该为我做检查,intellisense 应该帮助我填写组名。
这是tinker的操场链接。
解决方案
如果您检查select
您创建的类型,它是GroupedItems<Car, string | number>
. 所以我们可以看到问题出在TGroupKeys
. 我们希望它是'renault' | 'peugeot'
,而不是我们有string | number
。
您的接口IObjectMap
有一个索引签名,这意味着它应该包含任何字符串的值。CarMakers
extends每个字符串(以及数字)也是IObjectMap
如此keyof CarMakers<unknown>
,而不仅仅是对象的特定键。对于每个扩展的对象都是如此IObjectMap
。
根据打字稿文档,
如果您有一个带有字符串索引签名的类型,则 keyof T 将是 string | 数字(不仅仅是字符串,因为在 JavaScript 中,您可以使用字符串(object[“42”])或数字(object[42])来访问对象属性)。
我们需要删除该索引签名。事实上,我们可以在任何地方删除它。
而不是扩展keyof IObjectMap<unknown>
,TMapKeys
并且TGroupKeys
应该扩展string
或者PropertyKey
- 一个内置类型,它是所有有效属性键 ( string | number | symbol
) 的联合。
通过这些更改,我们现在得到了所需的错误:
Type '"audi"' is not assignable to type '"renault" | "peugeot"'
Object literal may only specify known properties, and 'audi' does not exist in type 'Record<"renault" | "peugeot", GetterFunc<Car, string>>'
推荐阅读
- powershell - powershell csv 从另一列中减去一列日期值并将结果写入第三个,其中 1 天等于 24 小时
- java - 如何使用 QueryDsl 在查询中按 id 删除重复行
- monaco-editor - 向摩纳哥编辑器添加更多语义突出显示
- azure - Azure 租户创建 - 谁可以创建?
- sql-server - 无法连接到 LocalDB 2019 的共享实例
- ruby-on-rails - 为什么Rails在线程中止时会悄悄地回滚事务?
- kubernetes-helm - helm: 'lookup' 函数总是返回空地图
- groovy - Jmeter Groovy 在使用 MSAL4J 时报告 NoSuchMethodError: ResourceOwnerPasswordCredentialsGrant
- linear-programming - 我怎样才能最好地使用我的目标函数来快速找到*一个*可行的解决方案(Gurobi)?
- windows - 批次 | 为什么在管理员中运行的脚本不能访问其他脚本?