TypeScript TS2590:表达式产生了一个过于复杂的联合类型,无法使用简单文件表示,建议使用元组,

lndjwyie  于 2个月前  发布在  TypeScript
关注(0)|答案(6)|浏览(37)

🔎 搜索词

TS2590。
关于TS2590有几个问题,但没有一个特定的、简单的复现代码。

🕗 版本与回归信息

这个版本在 5.0.0-dev.202211175.0.0-dev.20221118 之间发生了变化。

⏯ Playground链接

https://www.typescriptlang.org/dev/bug-workbench/?ssl=2&ssc=16&pln=2&pc=1#code/PTAEAEDMEsBsFMB2BDAtvAXKA9o+A6AFwGcBYAKBAlmgCMt5i8APQi6VAB2wCdDQA3tELxUAGlCEAnp3igAKgFdOCAJIjUAX1CQe2VKADk+YIQDu2QwG4KFAMa5i-YaIDC2TlKxKV8daNAAXlAXVBtyCiooOCQ0TEkLIjJyeGZuPlAAE3g7WGQeOQdEJxCNb2U1DXCKVPT+aVkFCr8NAB4fBCDQAG0ARgBdCQAFPVk+KS7DAAZDAD4ugQoASAA3ZFhFeI65VJFETOIegdAAHx6AJn7lpYB+UBGPeHHQXaQDoxnTo2J9eEMvwyQXgAUWQdgAFv8zoZUMhOFCjDBYCIeAjDAVMoo7H8ARisfAAErQADm4MIaJg+wp0H2qn2qWpsFgaIcngA6sJwTS0UhCDxoIw0QBreBSYhotYbQUAmm5RTZcUAyB5QgAWThFJVaOQ5Out3uoye0herDehxFUmwkCavlAd223QeY2k-VAWEU9Mp8Eyevdnpp3uufuyXsy4U0ViAA

💻 代码

tsc --lib esnext one.ts 
one.ts:3:7 - error TS2590: Expression produces a union type that is too complex to represent.

3 const itemCopy: TupleItem = item;
        ~~~~~~~~

Found 1 error in one.ts:3
// one.ts
import {item, type TupleItem} from './two';

const itemCopy: TupleItem = item;
// two.ts
export declare const item: TupleItem;

export type TupleItem<Tuple = [1], Property = '0'> = {
	value: Tuple extends [1] | [2]
		? Property extends '0' | 'some' | 'forEach' | 'map' | 'filter' | 'reduce' | 'reduceRight' | 'find' | 'findIndex' | 'fill' | 'copyWithin' | 'entries' | 'keys' | 'values' | 'includes' | 'flatMap' | 'flat' | 'at'
			? Property extends keyof Tuple ? Tuple[Property] : undefined
			: undefined
		: undefined;
};

🙁 实际行为

由于: TS2590: Expression produces a union type that is too complex to represent.,类型失败。

🙂 预期行为

上面的类型相当简单。即使联合类型有大约20个值,也不应该足以使类型失败。

关于这个问题的附加信息:
上面的代码中的小变化将无法重现错误,例如:

  • 将两个文件合并成一个文件
  • 不使用 declare const
  • 不使用泛型,或者不使用泛型默认类型
  • 使用 Tuple extends ... 而不是 {value: Tuple extends ...}
  • 不使用 --lib esnext
92dk7w1h

92dk7w1h1#

即使将所有内容放在一个文件中,这段代码似乎完全不起作用:
TS 实验场
类型 'Property' 不能用于索引类型 'Tuple'。

3qpi33ja

3qpi33ja2#

感谢你查看这个问题@fatcerberus。
这是一个不同的错误。这个问题可以通过使用以下代码来解决:

export type TupleItem<Tuple = [1], Property = '0'> = {
	value: Tuple extends [1] | [2]
		? Property extends '0' | 'some' | 'forEach' | 'map' | 'filter' | 'reduce' | 'reduceRight' | 'find' | 'findIndex' | 'fill' | 'copyWithin' | 'entries' | 'keys' | 'values' | 'includes' | 'flatMap' | 'flat' | 'at'
			? Property extends keyof Tuple ? Tuple[Property] : undefined
			: undefined
		: undefined;
};

这个失败是预期的,并且与 Property 是否可以索引 Tuple 有关。
上面的问题与 TypeScript 创建一个大的联合类型有关,这会导致类型检查崩溃,这是不同的。要能够重现它,似乎需要使用两个完全相同的文件(与上述相同)。

g9icjywg

g9icjywg3#

这个错误是可以预料到的,并且与 Property 是否可以索引 Tuple 有关。

在 OP 中编写的代码产生了这个错误,包括将其拆分为两个文件时。这很令人困惑,因为这不是你报告的错误信息,而且看起来像是重现错误本身就有问题。

值得注意的是,我无法使用 bug workbench(与 playground 不同,它确实支持多个文件)复现 "too-large-union-type" 错误:
Bug Workbench

aurhwmvo

aurhwmvo4#

OP中编写的代码产生了这个错误,包括将其拆分为两个文件时。这很令人困惑,因为这不是你报告的错误信息,而且看起来像是重现错误本身。
我已经更新了原始问题以反映这一点,并避免与另一个无关的错误产生混淆。
值得注意的是,我无法使用bug workbench(与playground不同,它支持多个文件)复现“过大的联合类型”错误:
哦,我不知道Bug Workbench支持多个文件。
你无法复现这个错误,因为它需要使用--lib esnext
以下是使用Bug Workbench复现此错误的示例。
该链接有效。然而,有趣的是,如果你点击URL(添加?lib=lib.esnext.d),然后刷新页面,Bug Workbench将无法加载。

polkgigr

polkgigr5#

快速笔记
堆栈看起来像这样
我们正在测试

TupleItem<[1], "0"> --assignable to--> TupleItem

为了做到这一点,我们使用两种标记类型的示例化计算 TupleItem 的方差

TupleItem<?_1, Property> --assignable to--> TupleItem<?_2, Property>

然后我们在 .value 上进行结构比较,这导致检查

? extends [1] | [2] ? Property extends "0" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight" | "find" | "findIndex" | "fill" | "copyWithin" | ... 6 more ... | "at" ? Property extends keyof ? ? ?[Property] : undefined : undefined : undefined

--assignable to-->

? extends [1] | [2] ? Property extends "0" | "some" | "forEach" | "map" | "filter" | "reduce" | "reduceRight" | "find" | "findIndex" | "fill" | "copyWithin" | ... 6 more ... | "at" ? Property extends keyof ? ? ?[Property] : undefined : undefined : undefined

最终达到了极限情况

// We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of
                    // the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type
                    // exceeds 100000 constituents, report an error.
                    if (!checkCrossProductUnion(typeSet)) {

因为 typeSet 具有所有这些类型:

'1 | 2'
'((predicate: (value: 1, index: number, array: 1[]) => unknown, thisArg?: any) => boolean) | ((predicate: (value: 2, index: number, array: 2[]) => unknown, thisArg?: any) => boolean)'
'((callbackfn: (value: 1, index: number, array: 1[]) => void, thisArg?: any) => void) | ((callbackfn: (value: 2, index: number, array: 2[]) => void, thisArg?: any) => void)'
'(<U>(callbackfn: (value: 1, index: number, array: 1[]) => U, thisArg?: any) => U[]) | (<U>(callbackfn: (value: 2, index: number, array: 2[]) => U, thisArg?: any) => U[])'
'{ <S extends 1>(predicate: (value: 1, index: number, array: 1[]) => value is S, thisArg?: any): S[]; (predicate: (value: 1, index: number, array: 1[]) => unknown, thisArg?: any): 1[]; } | { <S extends 2>(predicate: (value: 2, index: number, array: 2[]) => value is S, thisArg?: any): S[]; (predicate: (value: 2, index...'
'{ (callbackfn: (previousValue: 1, currentValue: 1, currentIndex: number, array: 1[]) => 1): 1; (callbackfn: (previousValue: 1, currentValue: 1, currentIndex: number, array: 1[]) => 1, initialValue: 1): 1; <U>(callbackfn: (previousValue: U, currentValue: 1, currentIndex: number, array: 1[]) => U, initialValue: U): U;...'
'{ (callbackfn: (previousValue: 1, currentValue: 1, currentIndex: number, array: 1[]) => 1): 1; (callbackfn: (previousValue: 1, currentValue: 1, currentIndex: number, array: 1[]) => 1, initialValue: 1): 1; <U>(callbackfn: (previousValue: U, currentValue: 1, currentIndex: number, array: 1[]) => U, initialValue: U): U;...'
'{ <S extends 1>(predicate: (value: 1, index: number, obj: 1[]) => value is S, thisArg?: any): S | undefined; (predicate: (value: 1, index: number, obj: 1[]) => unknown, thisArg?: any): 1 | undefined; } | { ...; }'
'((predicate: (value: 1, index: number, obj: 1[]) => unknown, thisArg?: any) => number) | ((predicate: (value: 2, index: number, obj: 2[]) => unknown, thisArg?: any) => number)'
'((value: 1, start?: number | undefined, end?: number | undefined) => [1]) | ((value: 2, start?: number | undefined, end?: number | undefined) => [2])'
'((target: number, start: number, end?: number | undefined) => [1]) | ((target: number, start: number, end?: number | undefined) => [2])'
'(() => IterableIterator<[number, 1]>) | (() => IterableIterator<[number, 2]>)'
'(() => IterableIterator<number>) | (() => IterableIterator<number>)'
'(() => IterableIterator<1>) | (() => IterableIterator<2>)'
'((searchElement: 1, fromIndex?: number | undefined) => boolean) | ((searchElement: 2, fromIndex?: number | undefined) => boolean)'
'(<U, This = undefined>(callback: (this: This, value: 1, index: number, array: 1[]) => U | readonly U[], thisArg?: This | undefined) => U[]) | (<U, This = undefined>(callback: (this: This, value: 2, index: number, array: 2[]) => U | readonly U[], thisArg?: This | undefined) => U[])'
'(<A, D extends number = 1>(this: A, depth?: D | undefined) => FlatArray<A, D>[]) | (<A, D extends number = 1>(this: A, depth?: D | undefined) => FlatArray<A, D>[])'
'((index: number) => 1 | undefined) | ((index: number) => 2 | undefined)'

我相信 esnext 中的定义接近底部;这里的许多联合意味着与这些定义不存在时的交叉积相比,非常大。

pcww981p

pcww981p6#

不确定这是否可以"修复"。TupleItem的期望行为是什么?可能可以通过不执行此计算的方式编写它。

相关问题