首页 > 解决方案 > 获取 TypeScript 联合字段的类型

问题描述

我有一个看起来像这样的联合类型:

type Option1 = {
    items: string[];
}
type Option2 = {
    delete: true;
}
type Combined = Option1 | Option2;

我想用items字段的类型定义一个新变量:

const items_variable:Combined["items"] = ["a", "b"];

但这给了我错误error TS2339: Property 'items' does not exist on type 'Combined'

如果我使用的是值而不是类型(我所看到的所有现有答案都假设),我可以在这里使用类型缩小,但是我不明白如何在现有类型上做到这一点。

Option2如果定义它似乎也可以按预期工作,items但这对我的用例没有意义。

我应该使用什么类型items_variable

标签: typescript

解决方案


这就是 TS 工会的工作方式。这是设计使然。

type Option1 = {
    items: string[];
}
type Option2 = {
    delete: true;
}
type Combined = Option1 | Option2;

type Keys = keyof Combined; // never

如您所见,keyof Combined返回never- 空键集。

因为Option1andOption2没有任何共同的 props,所以 TS 不确定允许什么属性。

假设您有一个期望Option1or的函数Option2。为了以Combined安全的方式使用,您应该使用自定义类型保护

type Option1 = {
    items: string[];
}
type Option2 = {
    delete: true;
}
type Combined = Option1 | Option2;

type Keys = keyof Combined; // never

const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
    : obj is Obj & Record<Prop, unknown> =>
    Object.prototype.hasOwnProperty.call(obj, prop);

const handle = (union: Combined) => {
    if (hasProperty(union, 'items')) {
        const option = union; // Option1
    } else {
        const option = union; // Option2
    }

}

或者您可以添加公共属性:

type Option1 = {
    tag: '1',
    items: string[];
}
type Option2 = {
    tag: '2',
    delete: true;
}
type Combined = Option1 | Option2;

type CommonProperty = Combined['tag'] // "1" | "2"

const handle = (union: Combined) => {
   if(union.tag==='1'){
       const option = union // Option1
   }
}

另一种替代方法是使用StrictUnion.

type Option1 = {
    items: string[];
}
type Option2 = {
    delete: true;
}
type Combined = Option1 | Option2;

// credits goes https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type UnionKeys<T> = T extends T ? keyof T : never;

type StrictUnionHelper<T, TAll> =
    T extends any
    ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;

type StrictUnion<T> = StrictUnionHelper<T, T>

type Union = StrictUnion<Combined>

const items_variable: Union['items'] = ["a", "b"]; // string[] | undefined

您可能已经注意到,有一个小缺点,items_variable可能也是undefined. 这是因为工会的性质。它可以是一个值,也可以是另一个值。


推荐阅读