为什么我的 TypeScript 报告联合类型错误?

Why does my TypeScript report an error for a union type?

我有:

type: 'WEBSOCKET' | 'HTTP_STREAM';
connection: WebSocketType | HTTPStreamType;

其中:

interface Header {
  key: string;
  val: string;
}

export interface WebSocketType {
  websocketUrl: string;
}

export interface HTTPStreamType {
  streamUrl: string;
  method: 'get' | 'put';
  headers: Header[];
}

我正在尝试:

        if (newStream.type === 'WEBSOCKET') {
          if (newStream?.connection?.websocketUrl?.length > 0) {
            return setIsNextDisabled(false)
          }
        }

但我得到的错误是:

Property 'websocketUrl' does not exist on type 'WebSocketType | HTTPStreamType'.
  Property 'websocketUrl' does not exist on type 'HTTPStreamType'.

我认为 guard 会起作用,但它不起作用。

我假设你有类似的东西:

interface MyData {
    type: 'WEBSOCKET' | 'HTTP_STREAM';
    connection: WebSocketType | HTTPStreamType;
}

根据此接口,您可以拥有 WEBSOCKETtypeHTTPStreamTypeconnection。你没有任何地方保证每个工会成员之间的任何联系。 typeconnection 都可以是联盟的任何一个成员。

这就是为什么打字稿认为你的守卫没有帮助。

相反,您想要一种不同类型的联合:

type MyData = {
    type: 'WEBSOCKET';
    connection: WebSocketType;
} | {
    type: 'HTTP_STREAM';
    connection: HTTPStreamType;
}

此类型表示 MyData 可能是 WEBSOCKETWebSocketType 连接的类型, 它可能是 HTTP_STREAMHTTPStreamType 连接。但它永远无法混合第一个 属性 和第二个

现在打字稿可以推断出对其中一个属性的检查允许知道另一个 属性 类型。

Playground 代码通过了类型检查。


通过一些重构,您可以避免重复通用属性 like so

interface MyCommonData {
    foo: string
    bar: number
}

interface WebSocketData extends MyCommonData {
    type: 'WEBSOCKET'
    connection: WebSocketType
}

interface HttpStreamData extends MyCommonData {
    type: 'HTTP_STREAM';
    connection: HTTPStreamType;
}

type MyData = WebSocketData | HttpStreamData

联合类型必须有一些公共字段,在您的情况下,这样的事情就可以了

interface Header {
  key: string;
  val: string;
}

export interface WebSocketType {
  url: string;
}

export interface HTTPStreamType {
  url: string;
  method: 'get' | 'put';
  headers: Header[];
}

export class Stream { 

  constructor(
    public _type: 'WEBSOCKET' | 'HTTP_STREAM',
    public connection: WebSocketType  | HTTPStreamType
  ) { }
}




let newStream = new Stream('WEBSOCKET', { url: 'myurl' })
let newStream2 = new Stream(
  'HTTP_STREAM',
  { url: 'meaw', method: 'get', headers: [
    { key: 'someheader', val: 'somevalue' }
  ]
  })

if (newStream._type === 'WEBSOCKET') {
  if (newStream.connection.url.length > 0) {
    //do stuff
  }
}

if (newStream2._type === 'HTTP_STREAM') {
  if (newStream2.connection.url.length > 0) {
    //do stuff
  }
}