提问人:Mauzzz0 提问时间:11/14/2023 最后编辑:Mauzzz0 更新时间:11/15/2023 访问量:84
打字稿。根据枚举输入参数返回不同的对象类型
Typescript. Return different objects type depending on enum input argument
问:
我有模板名称 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);
但是在函数体中,我不能只返回或,因为我得到了TS2322:{vk: 'someString'}
{viber:1}
TS2322:
类型 { vk: string; } 不可分配给 ReturnTypes[T]
类型 { vk: string; } 不可分配给类型 VkResponse 和 ViberResponse 属性 Viber 在类型 { vk: string; } 中缺失,但在类型 ViberResponse
中是必需的
为什么在这种情况下 TypeScript 不能进行类型缩小?为什么我需要使用“as ReturnTypes[T]”,如何避免它?我如何正确地将类型范围从特定缩小到特定或在 IF 块中显式定义某些(非联合)返回类型?key
T
.VK
.VIBER
TypeScript v5.2.2
节点 v20.8.0
这个也不起作用:
switch (key) {
case KeyEnum.VIBER:
return { viber: 1 }; <<- The same TS2322 error
}
答:
您可能正在寻找的是函数重载,在您的情况下,它将如下所示:
enum KeyEnum {
VK = 'VK',
VIBER = 'VIBER',
}
type VkResponse = {vk: string};
type ViberResponse = {viber: number};
function fetchTemplate(key: KeyEnum.VIBER): ViberResponse;
function fetchTemplate(key: KeyEnum.VK): VkResponse;
function fetchTemplate(key: KeyEnum) {
if (key === KeyEnum.VK) {
return { vk: 'someString' };
}
if (key === KeyEnum.VIBER) {
return { viber: 1 }
}
}
const vkRes: VkResponse = fetchTemplate(KeyEnum.VK);
const viberRes: ViberResponse = fetchTemplate(KeyEnum.VIBER);
评论
目前,基于控制流的缩小(如/或/块)不能很好地与泛型一起使用。问题在于,虽然检查会缩小 的类型,但它不会影响泛型类型参数(在您的示例中)。根据 microsoft/TypeScript#33014 中的要求,这被视为缺少的功能。除非实现,否则您需要解决它。switch
case
if
else
key === KeyEnum.VK
key
T
现在,如果要使用泛型函数并返回索引访问类型,例如 ,则需要实际执行索引操作:即获取类型的对象,并在 类型的键处读取属性。从概念上讲,对于您的示例,如下所示:ReturnTypes[T]
ReturnTypes
T
const fetchTemplate = <T extends KeyEnum>(key: T): ReturnTypes[T] => {
return {
[KeyEnum.VK]: { vk: 'someString' },
[KeyEnum.VIBER]: { viber: 1 }
}[key]
}; // okay
这样做的一个可能问题是,它要求您预先计算每个可能的返回值。如果调用 ,该对象仍将计算,然后将其丢弃。对于像这样的简单情况,这可能没什么大不了的,但是如果你的代码有副作用或计算成本很高,那么你可以重构为使用 getter,以便只运行相关的代码:fetchTemplate(KeyEnum.VK)
{viber: 1}
const fetchTemplate = <T extends KeyEnum>(key: T): ReturnTypes[T] => {
return {
get [KeyEnum.VK]() {
return { vk: 'someString' };
},
get [KeyEnum.VIBER]() {
return { viber: 1 };
}
}[key]
};
在这里,如果你调用 ,只有 getter for 永远不会运行。fetchTemplate(KeyEnum.VK)
KeyEnum.VK
希望有一天 microsoft/TypeScript#33014 能够实现,因为这种重构虽然功能强大,但可能会令人困惑。
下一个:如何使用mapster映射枚举
评论
T extends "first" ? FirstType : T extends "second" ? SecondType :