解构/访问对象联合类型上可能存在或不存在的属性

问题描述 投票:0回答:5

我收到以下错误:

type Union = { type: "1"; foo: string } | { type: "2"; bar: number };

function doSomething = (object: Union) => {
  const { foo } = object
  //      ^ TS2339: Property 'foo' does not exist on type 'Union'.
  console.log(object.bar)
  //                 ^ TS2339: Property 'bar' does not exist on type 'Union'.
}

期望的结果:

typeof foo === string | undefined
typeof bar === number | undefined

如何在没有显式类型保护的情况下访问属性,例如:

const foo = o.type === 1 ? o.foo : undefined
const bar = o.type === 2 ? o.bar : undefined

这对我来说并不是一个真正的选择,因为我正在与大型联合合作,其中目标属性可能会或可能不会出现在许多对象上,这将是一团糟。

我还有哪些其他选择?

typescript destructuring union-types object-destructuring
5个回答
10
投票

您可以为未使用的属性设置

never
,然后 TS 可以将这些属性的类型理解为可选。

type Type1 = { type: "1"; foo: string, bar?: never }
type Type2 = { type: "2"; foo?: never, bar: number }    
type Union = Type1 | Type2

const doSomething = (object: Union) => {
    const { type, foo, bar } = object
    console.log(type, foo, bar)
}

doSomething({ type: "1", foo: "foo" }) // [LOG]: "1",  "foo",  undefined 
doSomething({ type: "2", bar: 2 }) // [LOG]: "2",  undefined,  2 

TS游乐场链接: https://www.typescriptlang.org/play?#code/C4TwDgpgBAKuEEYoF4oG8qkgLigIgTwG4oAzAe3NwGdgAnASwDsBzAGigCMBDOgflxMIANwh0oAXwCwAKCzQ4kAEwp0meLjxLiZSgKhDRdDjzqCArgFtOYyVHtRZ8q AFUmDck1WLEUAD6w8EqysgDGnrRQACbkAMrklhDAABbMLKoAFOScAFYQocC4bh5MAJQoAHzosg5Q4UyRGPIcFOQmvHao2XkFNQ711OQANhaAAdEPkLBnNum1cvKWy0jKyMfGJKWkZTRr4hC2Umq14kos yawlJqazb6jj42u1mUCoSZ0A


3
投票

检查访问对象类型联合中的属性对于未在所有联合成员上定义的属性失败#12815

这里的问题是,因为 B 没有声明 a 属性,所以它在运行时可能有任何可能类型的 a 属性(因为您可以将具有任何属性集的对象分配给 B,只要它具有字符串类型的 b 属性)。您可以在 B 中显式声明 a: undefined 属性来使其工作(从而确保 B 不会有一些随机的 a 属性):

type A = { a: string; } 
type B = { b: string; a: undefined }
type AorB = A | B;

declare const AorB: AorB;

if (AorB.a) {
   // Ok
}

2
投票

我发现的最方便的方法是根据变量的类型来转换变量。

type Type1 = { type: "1"; foo: string }
type Type2 = { type: "2"; bar: number }    
type Union = Type1 | Type2

function doSomething = (object: Union) => {
    const { foo } = object as Type1
    const { bar } = object as Type2
    const { type } = object  
}

1
投票

这种行为有点道理,因为 TS 不知道它正在处理 Union 中的哪个对象,并且在某些情况下该属性不存在。

我不确定这是否是您正在寻找的,但您可以尝试类似的方法

type Union = { type: "1"; foo: string } | { type: "2"; bar: number };

function doSomething = (object: Union) => {
  if ('foo' in object) {
    const { foo } = object
  }

  if ('bar' in object) {
    console.log(object.bar)
  }
}

这里是游乐场


0
投票

我这样做了:

type Union<T1 extends object, T2 extends object> = {
  [K in keyof T1]: K extends keyof T2 ? T1[K] | T2[K] : T1[K] | undefined;
} & {
  [K in keyof T2]: K extends keyof T1 ? T1[K] | T2[K] : T2[K] | undefined;
};
© www.soinside.com 2019 - 2024. All rights reserved.