Typescript 如何为区分的联合类型数组编写typeguard?
我想做的事情的最小代码复制:Typescript 如何为区分的联合类型数组编写typeguard?,typescript,Typescript,我想做的事情的最小代码复制: const handlers = { foo: (s: string) => s.length, bar: (n: number) => n.toFixed(2) } type DataMap = { [P in keyof typeof handlers]: { type: P, data: Parameters<typeof handlers[P]>[0] } } type Block = DataMa
const handlers = {
foo: (s: string) => s.length,
bar: (n: number) => n.toFixed(2)
}
type DataMap = {
[P in keyof typeof handlers]: {
type: P,
data: Parameters<typeof handlers[P]>[0]
}
}
type Block = DataMap[keyof DataMap];
const data: Array<Block> = []
data.forEach(block => {
if (block.type in handlers) {
// Error: Argument of type 'string | number' is not assignable to parameter of type 'never'.
handlers[block.type](block.data)
}
})
现在forEach error中有一个错误:“string | number”类型的参数不能分配给“never”类型的参数
我该如何为这种情况写typeguard
编辑:
如果现在除了断言之外没有解决方案,那么也许有更好的方法来构造这样的数据?我试图重写它,因为块类型似乎是以错误的方式定义的;我最后得到的是:
interface Block<P extends keyof typeof handlers>
{
type: P,
data: Parameters<typeof handlers[P]>[0]
}
const data: Array<Block> = []
现在,您可以在最后一行找到问题的根源,这会引发错误:
泛型类型块需要1个类型参数。ts2314
只有当类型具有将属性类型和数据逻辑地联系在一起的泛型参数时,它才有意义。因此,一旦您尝试使其成为一个数组,这就变得不可能了
您可能必须重新构造对象,或者忽略类型错误。我试图重写它,因为块类型的定义似乎是错误的;我最后得到的是:
interface Block<P extends keyof typeof handlers>
{
type: P,
data: Parameters<typeof handlers[P]>[0]
}
const data: Array<Block> = []
现在,您可以在最后一行找到问题的根源,这会引发错误:
泛型类型块需要1个类型参数。ts2314
只有当类型具有将属性类型和数据逻辑地联系在一起的泛型参数时,它才有意义。因此,一旦您尝试使其成为一个数组,这就变得不可能了
您可能必须重新构造对象或忽略类型错误。这是我一直称之为相关记录类型的另一个实例,TypeScript编译器无法很好地解释这一点。有关详细信息,请参见,但问题是编译器看到block.type和block.data都是类型的并集,但它不理解只有一些可能性可以同时发生,而其他可能不会。如果您引导编译器遍历这些案例,就可以了:
data.forEach(block => {
if (block.type in handlers) {
if (block.type === "foo") {
handlers[block.type](block.data)
} else {
handlers[block.type](block.data)
}
}
})
因为在每一种情况下,它都可以将障碍缩小到一个受歧视工会的一个成员。但它不能从总体上对此进行推理。您可能只需要使用类型断言,然后继续,比如:
data.forEach(block => {
(handlers[block.type] as (args: Block['data']) => any)(block.data)
})
这是我一直称之为相关记录类型的另一个实例,TypeScript编译器无法很好地解释它。有关详细信息,请参见,但问题是编译器看到block.type和block.data都是类型的并集,但它不理解只有一些可能性可以同时发生,而其他可能不会。如果您引导编译器遍历这些案例,就可以了:
data.forEach(block => {
if (block.type in handlers) {
if (block.type === "foo") {
handlers[block.type](block.data)
} else {
handlers[block.type](block.data)
}
}
})
因为在每一种情况下,它都可以将障碍缩小到一个受歧视工会的一个成员。但它不能从总体上对此进行推理。您可能只需要使用类型断言,然后继续,比如:
data.forEach(block => {
(handlers[block.type] as (args: Block['data']) => any)(block.data)
})
在原始示例中,基本上是块的副本,基本上是块的副本,是TypeScript中非常常见的数据结构。为什么这是一个错误的方法,如何使它通用解决任何问题?请注意,即使在您的版本中,您也可以有data:Array,它可以恢复受歧视的并集。是的,我也不太明白。您的示例中的块将生成与我的版本基本相同的联合。@jcalz:我知道这是一个有区别的联合,我一直在使用它们。它的定义方法是错误的,因为DataMap只用于提取其属性的类型,而属性不是在映射本身中定义的,而是在另一个对象中定义的,因此您最好完全跳过映射。泛型是必需的,因为数据依赖于类型。当然,您可以枚举所有可能的值,但这会占用数组进行迭代的大部分便利。@Danila:而且它还会丢失一般处理每个块所需的所有信息。我的意思是,如何生成这个并集、使用map或使用自己的方式都无关紧要,或手动硬编码所有选项。最终的结果是一样的,问题是如何使用这种结构来避免任何错误。不过,感谢您的示例,这样写可能会更容易。原始示例中的块是一种非常常见的TypeScript数据结构。为什么这是一个错误的方法,如何使它通用解决任何问题?请注意,即使在您的版本中,您也可以有data:Array,它可以恢复受歧视的并集。是的,我也不太明白。您的示例中的块将生成与我的版本基本相同的联合。@jcalz:我知道这是一个有区别的联合,我一直在使用它们。它的定义方法是错误的,因为DataMap只用于提取其属性的类型,而属性不是在映射本身中定义的,而是在另一个对象中定义的,因此您最好完全跳过映射。泛型是必需的,因为数据依赖于类型。当然,您可以枚举所有可能的值
但这就剥夺了使用数组进行迭代的大部分便利。@Danila:而且它也会丢失您通常处理每个块所需的所有信息。我的意思是,无论您如何生成这个并集,使用map,或使用自己的方式,或手动硬编码所有选项。最终的结果是一样的,问题是如何使用这种结构来避免任何错误。不过,感谢您的示例,这样写可能更容易。