我有模板名enum,对于每个enum值,函数fetchTemplate
必须返回它自己的响应类型。
enum KeyEnum {
VK = 'VK',
VIBER = 'VIBER',
}
type VkResponse = { vk: string };
type ViberResponse = { viber: number };
type ReturnTypes = {
[KeyEnum.VK]: VkResponse;
[KeyEnum.VIBER]: ViberResponse;
};
字符串
我用这个签名创建了函数fetchTemplate
:
const fetchTemplate = <T extends KeyEnum>(key: T): ReturnTypes[T]
型
而这个身体:
const fetchTemplate = <T extends KeyEnum>(key: T): ReturnTypes[T] => {
if (key === KeyEnum.VK) {
return { vk: 'someString' }; <<- Here i got TS2322 error (description below)
}
if (key === KeyEnum.VIBER) {
return { viber: 1 } as ReturnTypes[T]; <<- No problems, but i dont want use "as"
}
};
# Unnecessary type definition in the left, just for testing
const vkRes: VkResponse = fetchTemplate(KeyEnum.VK);
const viberRes: ViberResponse = fetchTemplate(KeyEnum.VIBER);
型
但是在函数体中我不能只返回{vk: 'someString'}
或{viber:1}
,因为我得到了TS 2322:
TS2322:
类型{ vk:string; }不能分配给类型ReturnTypes[T]
类型{ vk:string; }不能分配给类型VkResponse & ViberResponse
类型{ vk:string; }中缺少属性viber,但ViberResponse类型中需要该属性
为什么TypeScript在这种情况下不能做类型收缩?为什么我需要使用“as ReturnTypes[T]",如何避免它?我如何正确地将key
类型从T
缩小到特定的.VK
或.VIBER
,并在IF块中显式定义某些(非联合)返回类型?
TypeScript v5.2.2
节点v20.8.0
这个也不行:
switch (key) {
case KeyEnum.VIBER:
return { viber: 1 }; <<- The same TS2322 error
}
型
2条答案
按热度按时间k4aesqcs1#
当前基于控制流的收缩(如
switch
/case
或if
/else
块)不能很好地处理generics。问题是,虽然检查key === KeyEnum.VK
会缩小key
的类型,但它不会影响泛型类型参数。(在您的示例中是T
)。这被认为是一个缺失的特性,正如microsoft/TypeScript#33014中所要求的那样。除非实现了它,否则您需要解决它。现在,如果你想使用一个泛型函数并返回一个
ReturnTypes[T]
这样的indexed access type,你需要实际执行一个索引操作:也就是说,获取一个ReturnTypes
类型的对象,并读取T
类型的键处的属性。从概念上讲,你的例子看起来像这样:字符串
一个可能的问题是,它需要你预先计算每一个可能的返回值。如果你调用
fetchTemplate(KeyEnum.VK)
,对象仍然会计算{viber: 1}
,然后把它扔掉。对于这样一个简单的情况,这可能没什么大不了的,但是如果你的代码有副作用或者计算起来很昂贵,那么你可以重构为使用getters,这样只有相关的代码才能运行:型
在这里,如果调用
fetchTemplate(KeyEnum.VK)
,只有KeyEnum.VK
的getter永远不会运行。希望有一天microsoft/TypeScript#33014能够实现,因为这种重构虽然功能强大,但可能会引起混淆。
Playground链接到代码
koaltpgm2#
你可能需要的是函数重载,在你的例子中看起来像这样:
字符串