这是 TypeScript 中的一个已知痛点,请参阅微软/TypeScript#13995 https://github.com/microsoft/TypeScript/issues/13995 and 微软/TypeScript#24085 https://github.com/microsoft/TypeScript/issues/24085。问题是基于控制流的类型分析 https://github.com/microsoft/TypeScript/pull/8010不适用于泛型类型参数。
If key
被声明为类型IdentifierEnum
,然后检查if (key === IdentifierEnum.ID1) {...}
会缩小类型key
在 - 的里面{...}
块成为Identifier.ID1
:
const k: IdentifierEnum = key;
if (k === IdentifierEnum.ID1) {
k; // const k: IdentifierEnum.ID1
} else {
k; // const k: IdentifierEnum.ID2
}
这就是控制流分析。现在这不会发生key
是泛型类型K
,但即使它确实如此,它也不会帮助你:
if (k === IdentifierEnum.ID1) {
return 1; // ERROR!
}
这是因为,无论如何,从 TypeScript 3.8 开始,即使value类型的K
缩小了,类型K
本身就是not。编译器从来不会说“如果key
is IdentifierEnum.ID1
, then K
is IdentifierEnum.ID1
因此,如果您希望编译器为您验证类型安全,则不能使用这种基于控制流的实现。
TypeScript 的未来版本可能会以某种方式改善这一点,但这很棘手。一般来说,只是因为一个值x
类型的X
可以缩小到键入Y
,这并不意味着类型X
本身可以缩小。如果您有多个类型值,这一点是显而易见的X
围坐。但无论如何,就目前而言,这是需要解决的问题。
您已经探索过类型安全性较差的方法,并且对此感到不满意:类型断言和重载签名,因此我将不再写出您将如何实现它。
在这里做一些相对类型安全的事情的唯一方法是放弃控制流分析,而使用索引操作。编译器足够聪明,可以意识到如果你有一个值t
类型的T
和一个值k
类型的K extends keyof T
,即该值t[k]
将是类型T[K]
。就你而言,T
is DataType
。因此,您需要该类型的值来索引:
class LoadingService {
loadData<K extends IdentifierEnum>(key: K): DataType[K] {
return {
get [IdentifierEnum.ID1]() { return 1; },
get [IdentifierEnum.ID2]() { return "" }
}[key]; // okay
}
}
上述类型检查。请注意,我将属性实现为getters https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get。你不必这样做;你可以直接写:
return {
[IdentifierEnum.ID1]: 1,
[IdentifierEnum.ID2]: ""
}[key];
但是 getter 版本允许您进行更任意的计算,并且知道只有对应于key
将会得到评价。
Playground 代码链接 https://www.typescriptlang.org/play/#code/KYOwrgtgBAkgJqALgSwGbOAJwKLmgbwCgpYARARigF4oByGC2gGmLICZq6G3bCBfQoWQhEWVAEMAxsCilxicQBUAngAcZREgG14SNBhx4AdA3IBdAFxQ8AIywttukfqy5IJ0m0tQAzokzCAOb8gpIANuI+PlAAMgD24nBBAMpYAG7I0lCaUGEJcHIKADwA0lDAAB6iIHDRTijorngAfAAUANbAylYlAJRWhUpqwFolZtmCJCSScSB+UO1W9S6GkJydygDcrCRoUB3UVDTLjasQHuS9E1NT7ZtQAPQPUDNziAtLCM6nbuemO1A+OUwj4NACSHdHs9XvNFrAvg0DL8PGwAQIAXsDkdjgiVsjTFccjdMMBEGBMCAoOR7k8oNgAEr0gDy9IAhFA0ZMpiSyRTrjcoIFSVAdLifsZTGZWoSoDzyZTqYCHAKhe9RXpxe5uFKZXK+QAifWAtFaDZmbYkATomHvPKJFLpTLAKzxe0gQKpTAZLI0EDAADusXyDq9Tul2xtUDg8nElBodqS7s93uARgTg1aJyREoovRpz0QwystiwhEj0YUHHjwaTjukafyGazTS1njzUKghfUVj8AXdgkIQA