严格模式下的TypeScript 2.8.4
我有一个像这样的枚举:
export enum TabIndex {
Editor = 'editor',
Console = 'console',
Settings = 'settings',
Outputs = 'outputs'
}
我要在其中创建这样的地图:
tabs = new Map(<[string, string][]>Object.keys(TabIndex).map((key: any) => [TabIndex[key], key]));
稍后,我试图使用查询参数从该地图中获取特定成员,该参数应该在地图中可用:
const tabParam = this.urlSerializer.parse(this.location.path()).queryParams.tab; // this thign is either 'editor', 'console', 'settings', ... etc.
然后我做
if (this.tabs.has(tabParam)) { // at this point we know the tab is available
this.selectTab(TabIndex[this.tabs.get(tabParam)!]); // here, TS thinks the index may be undefined, that's why I'm using the non-null assertion operator "!"
}
此代码仍然使TS不满意。错误提示:
Element implicitly has an 'any' type because index expression is not of type 'number'.
是的,索引类型是字符串。但是我知道,那是应该的,因为枚举支持字符串值。有人知道如何让TS开心吗?
我做了一些研究,this issue评论建议使用keyof typeof
来解决此问题:
const tabParam: keyof typeof TabIndex = this.urlSerializer.parse(this.location.path()).queryParams.tab;
这只是让TypeScript再次感到不高兴:
Type 'string' is not assignable to type '"Editor" | "Console" | "Settings" | "Outputs"'
答案 0 :(得分:2)
我认为问题在于您要给地图指定类型:
Map<string, string>
。这是由您创建地图的方式引起的:new Map(<[string, string][]> ...)
。
这导致您收到错误:
Type 'string' is not assignable to type '"Editor" | "Console" | "Settings" | "Outputs"'
在下一个代码段中,您尝试通过使用键调用get
方法来使用该Map的值。但是,这会返回一个字符串(因为您地图的类型已定义为具有字符串类型的值。
this.selectTab(TabIndex[this.tabs.get(tabParam)!]);
但是,TabIndex[xxxx]
语句期望'xxxx'是以下值"Editor" | "Console" | "Settings" | "Outputs"
之一,您可以从上面的错误消息中扣除。
要解决此问题,您需要更改Map的类型,以便打字稿知道上面摘录中的“ xxxx”将始终是这些值之一。为此,您需要创建一个“那些特定字符串文字的联合类型”。幸运的是,打字稿为我们提供了一种从TabIndex
定义中提取那些内容的方法。
keyof typeof TabIndex
这将解析为正确键入Map所需的“字符串文字的联合类型”。
总而言之,将地图的创建更改为:
const tabs = new Map(<[string, keyof typeof TabIndex][]>Object.keys(TabIndex).map((key: any) => [TabIndex[key], key]));
这将确保可以传递给TabIndex [xxxx]的Map中的值始终是已知的字符串文字。
答案 1 :(得分:0)
问题在于,在定义typeParam
的行中,您要分配一个string
(我想象中的queryParam.tab
是字符串?或者是any
) keyof typeof TabIndex
。 string
可以包含任何字符串值,因此不能将其分配给仅接受四个特定值的变量。
但是,如果您确定tab
将一直是您所期望的,则可以断言:
type TabIndexKey = keyof typeof TabIndex;
const tabParam: TabIndexKey = this.urlSerializer.parse(this.location.path()).queryParams.tab as TabIndexKey.
如果queryParams.tab
是any
,则解决方案相同。
编辑: 为避免不隐含错误,枚举的索引表达式必须使用数字,而不是字符串。这意味着:
TabIndex['editor']; // error with not implicit any
TabIndex[0]; // correct
因此,我建议您更改枚举和tabs
映射的定义。不用分配字符串,而是分配数字:
export enum TabIndex {
Editor = 0,
Console = 1,
Settings = 2,
Outputs = 3
}
const tabs = new Map(<[string, number][]>Object.keys(TabIndex).map((key: any) => [key, TabIndex[key]]));
当然,这样做tabs
并不是真正的<string, number>
地图,因此我们在撒谎。实际上,它具有字符串和数字。数字键指向属性名称,字符串键指向它们的数字等效项。但是,我们会忘记它,因为您将仅使用字符串键。
现在,TabIndex[this.tabs.get(tabParam)!]
正常工作,因为this.tabs.get
返回了number
。
希望这对您有所帮助。