typescript - 为什么这种类型的联合在 typescript 中被解析为一个交集?

标签 typescript

我无法理解我遇到的 typescript 错误背后的机制 - 我确信有一些基本的东西在我头顶上飞过时发出“嗖”的声音。

我只找到了 this关于该主题的问题,但虽然错误看起来相同,但设置对我来说似乎完全不同。

这是我引用的定义,为简洁起见。

type ApexAxisChartSeries = {
  // ...
  data:
    | (number | null)[]
    | {
        x: any;
        y: any;
        // ...
      }[]
    | [number, number | null][]
    | [number, (number | null)[]][];
}[]

这是一个错误的函数,它接受一个 {x:number, y:number} 数组。

function composeSeries(input: { x: number; y: number }[]) {
  const series: ApexAxisChartSeries = [
    { name: "A", data: [] },
    { name: "B", data: [] },
  ];

  input.forEach((datum) => {
    series[0].data.push(datum); // <-- This line right here gives the error.
  });

  return series;
}

现在,让我感到困惑的是,根据类型定义,我希望 {x:number, y:number} 对象是 data 的完全有效元素>,但是 typescript 提示说

Argument of type '{ x: number; y: number; }' is not assignable to parameter of type number & { x: any; y: any; fillColor?: string | undefined; strokeColor?: string | undefined; meta?: any; goals?: any; } & [number, number | null] & [number, (number | null)[]]'.

Type '{ x: number; y: number; }' is not assignable to type 'number'.

我在这里想,如果类型定义使用联合,错误中的交集来自哪里

作为附加上下文:

  1. 不仅仅是对象出错了。如果我使用 [x,y] 元组或只是一个数字数组,编译器仍然会活跃起来
  2. 我正在使用 TS 4.5.4
  3. 我非常肯定此代码中存在问题:TS playground 给出了相同的错误

作为引用,这是完整的类型定义:

type ApexAxisChartSeries = {
  name?: string
  type?: string
  color?: string
  data:
    | (number | null)[]
    | {
        x: any;
        y: any;
        fillColor?: string;
        strokeColor?: string;
        meta?: any;
        goals?: any;
      }[]
    | [number, number | null][]
    | [number, (number | null)[]][];
}[]

非常感谢任何澄清!

编辑:可以在 TS Playground 上找到错误示例 here ,由@jcalz 提供

最佳答案

(T | U)[]T[] | 是有区别的你[]。第一种,每个数组元素可以是 TU 类型。在第二个(与您的情况相对应)中,数组只能包含任一类型的元素。

您必须问自己的问题是:当 TypeScript 不知道变量是哪种类型的数组时,它如何知道允许哪些元素?

const myArray: string[] | number[] = [];
myArray.push(5); // Argument of type 'number' is not assignable to parameter of type 'never'.

以上面的例子为例。无论如何,TypeScript 都会抛出错误,因为无论您向该数组推送什么,都不会符合其中一种可能的类型。

好消息是 TypeScript 编译器很聪明,并且在您的情况下发现可以派生出符合所有可能条件的类型。该类型恰好是所有可能的元素类型的交集,是满足要求的最基本类型。

您的选择是要么使数组允许任何可能的类型,要么使该系列成为扩展任何一种类型的泛型。

选项A:数组元素可以是多种类型之一

type ApexAxisChartSeries = {
  name: string,
  data:
    (number 
      | null 
      | { x: any; y: any; } 
      | [number, number | null]
      | [number, (number | null)[]]
    )[]
};

选项 B:泛型类型参数可以是多种类型之一

type ChartDataType = (number | null)
  | { x: any; y: any; } 
  | [number, number | null] 
  | [number, (number | null)[]];

type ApexAxisChartSeries<T extends ChartDataType> = {
  name: string,
  data: T[]
}[];

function composeSeries<T extends ChartDataType>(input: T[]) {
  const series: ApexAxisChartSeries<T> = [
    { name: "A", data: [] },
    { name: "B", data: [] },
  ];

  input.forEach((datum) => {
    const index = (true) ? 0 : 1;
    series[index].data.push(datum);
  });

  return series;
}

composeSeries([{ x:1, y: 1}]); // returns { name, data: { x, y }[] }[]

关于typescript - 为什么这种类型的联合在 typescript 中被解析为一个交集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71313168/

相关文章:

typescript - 如何 WebPack typescript 语言服务器扩展 LSP

javascript - 使用forwardRef创建的React组件上出现类型错误

angular - 在 Angular 5 应用程序中导入第三方 js 库 cq-prolyfill 的正确方法

Typescript 接口(interface)一个字段类型依赖于另一个使用接口(interface)交集的字段值 '&'

Angular2 Firebase 设置用户自定义属性

javascript - 在另一个指令的点击事件上重新计算 ngx-datatable

Angular 9 + CLI (TypeScript) - 如何停止生成 .spec.ts 测试文件

javascript - 了解 Javascript/Typescript 中的 findIndex

javascript - 动态更改下拉列表的选定值

javascript - 有 Elixir 的 typescript 吗?