【问题标题】:Object discriminated union types are merged when using fallback使用回退时合并对象区分的联合类型
【发布时间】:2020-07-30 03:58:59
【问题描述】:

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

我正在尝试使用可区分的联合定义一些object 类型,以便知道某些特定namevalue 的属性。但是,始终需要保留一个后备,以便当 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 使用基本类型,并且形状与IValueDef 相同,所以它正在合并类型......?目前,我对如何使其正常工作感到不知所措。非常感谢您有一双新鲜的眼睛。

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    如果您扩展 Merged 类型,您最终会得到如下类型:

    (
        | 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;
    }
    

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

    【讨论】:

    • 不幸的是,我没有那个选项。这是一个努力添加,如果不是类型安全,那么类型提示到一个公共插件 API,它是用 JS 设计的,并且已经被广泛使用......并且大多数使用它的插件不提供 @987654328 的类型@ 与他们的条目有关。我认为要做我想做的事,整个 API 都需要重新设计,这不是我的决定。不过,它确实需要它。感谢帮助
    • 查看我的答案,了解如何在不需要其他密钥的情况下指定回退。
    【解决方案2】:

    这是一个开始:playground

    我使用Exclude&lt;string, 'test1'|'test2'&gt; 表示“除test1 和test2 之外的所有字符串”。

    我不确定是否有可能在指定额外属性时使其出错。

    【讨论】:

    • 呃,同样的问题,真的。我可以忍受它不会引发错误;我有点期望它无论如何都不会,因为它仍然需要接受具有一些未知属性集的value 对象。至少,我希望得到准确的智能感知,因为目前它似乎充当了一个简单的联合,将value 的所有可能属性合并到不同的接口中。不过还是谢谢你
    猜你喜欢
    • 1970-01-01
    • 2021-09-03
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-02
    • 2020-02-16
    • 1970-01-01
    相关资源
    最近更新 更多