使用后备时,将对象区分的联合类型合并

时间:2020-04-16 19:14:42

标签: typescript typescript-generics

我正在定义一个需要接受stringobject类型的数组。 object类型必须包含两个属性:namevaluevalue必须是另一个对象,其中包含任意一组key: value对。

我正在尝试使用可区分的并集定义某些object类型,以便已知某些特定value的{​​{1}}的属性。但是,始终需要保留一个后备,以便在name不是已知的字符串文字时,name仍可以是任意集合。

到目前为止,我正在与之合作:

value

我遇到的问题是,当我在联合中包含interface IFallbackDef { name: string; value: object; } type ValueDef<TName extends string = string, TOptions extends object = {}> = { name: TName; value: TOptions; }; type Merged<TValueDef extends ValueDef> = (string | TValueDef | IFallbackDef)[]; interface ITest1Options { foo: string; bar: string; } interface ITest2Options { baz: string; qux: string; } const test: Merged< | ValueDef<'test1', ITest1Options> | ValueDef<'test2', ITest2Options> > = [ 'asdf', { name: 'test1', value: { foo: 'asdjfkl', bar: 'asdf', /** * Intellisense shows both sets of properties, * and typescript allows them all, too */ qux: 'asdfkljsdg' // This should be an error } }, { name: 'test2', value: { baz: 'blah', qux: 'test', /** * Intellisense shows both sets of properties, * and typescript allows them all, too */ foo: 'salfdj' // This should be an error } }, { name: 'asdf', value: { /** * Intellisense shows both sets of properties, * should show none. */ }, }, ] 时,各种IFallbackDef属性的所有类型都会合并。如果我排除value,则联合可以正常工作,但是IFallbackDef数组的最后一个索引将是错误的,因为test是未知的。

我假设因为name: 'asdf'使用基本类型,并且形状与IFallbackDef相同,所以它正在合并类型...?此刻,我对如何使其正常工作感到困惑。新鲜的眼睛将不胜感激。

2 个答案:

答案 0 :(得分:1)

如果展开“合并”类型,则最终得到的类型如下:

(
    | string
    | { name: "test1", value: { foo: string, bar: string } }
    | { name: "test2", value: { baz: string, qux: string } }
    | { name: string, value: object }
)[]

问题在于,这不是受| { name: string, value: object }歧视的联合。该行中断了区分的联合检查,因为string'test1''test2'都匹配,因此TypeScript不能区分。

您需要删除该行(例如,按照您的建议删除IFallbackDef),以使受歧视的工会正常工作。不幸的是,我不相信有一种解决方法可以使您实现以下目标:将具有特定键类型的事物归为特定值类型,但是将没有特定键类型之一的事物归为不同值类型

根据您的约束,一种选择是对后备使用不同的密钥。例如:

interface IFallbackDef {
    not-name: string;
    value: object;
}

这将允许对具有名称键的对象与具有非名称键的对象进行区分的联合类型检查。

答案 1 :(得分:0)

这是一个开始:playground

我用Exclude<string, 'test1'|'test2'>表示“除test1和test2以外的所有字符串”。

我不确定指定额外的属性是否有可能使它出错。