VSCode中TypeScript ReturnType返回联合类型顺序不稳定问题问询
1. TypeScript联合类型ReturnType的顺序异常
在VSCode中运行以下TypeScript代码时出现不符合预期的现象:
type A = () => 'a'; type B = () => 'b'; type Res1 = ReturnType<A | B>; type Res2 = ReturnType<B | A>;
我预期Res1和Res2的类型应该不同,分别为'a' | 'b'和'b' | 'a',但实际输出始终一致,有时是'a' | 'b',有时是'b' | 'a',只有重启VSCode窗口或TS服务才可能得到预期结果。这是不是VSCode的Bug?
2. 对类型顺序敏感的场景需求
虽然联合类型的顺序通常不影响使用,但在类似Promise.all的场景中,顺序至关重要:
Promise.all([a,b,c]).then(data=>{ const [a,b,c]=data; });
这里data的类型是按输入Promise数组的顺序排列的元组,而非无序的联合类型。现在需要实现一个RightType类型,使得输入的联合类型顺序不同时,输出的类型顺序也对应不同:
type RightType = xxx; type A = () => 'a'; type B = () => 'b'; type Res1 = RightType<A | B>; // 期望得到 'a' | 'b' type Res2 = RightType<B | A>; // 期望得到 'b' | 'a'
首先明确:这不是VSCode的Bug,而是TypeScript本身的设计行为——TypeScript中的联合类型是无序的,编译器会对联合类型的成员进行归一化处理,所以A | B和B | A在TypeScript看来是完全等价的类型,ReturnType的输出顺序只是编译器内部处理时的随机表现,并非Bug。
你提到的Promise.all场景,实际处理的是元组类型而非联合类型,元组是有序结构,所以Promise.all([p1, p2])的返回值类型是[T1, T2],这和联合类型的无序特性不冲突。
由于TypeScript不支持保留联合类型的原始顺序(联合类型本身就是无序集合),要实现顺序敏感的类型逻辑,需要用元组作为输入替代联合类型:
实现方案(基于元组输入)
如果需要模拟顺序对应的联合类型输出:
// 将元组中每个函数的返回值提取为元组,再转为联合类型 type TupleToUnion<T extends any[]> = T[number]; type RightType<T extends readonly (() => any)[]> = TupleToUnion<{ [K in keyof T]: T[K] extends () => infer R ? R : never; }>; // 使用示例 type A = () => 'a'; type B = () => 'b'; type Res1 = RightType<[A, B]>; // 推导逻辑为 'a' | 'b' type Res2 = RightType<[B, A]>; // 推导逻辑为 'b' | 'a'
注:TypeScript编译器显示联合类型时仍可能自动排序(比如按字母顺序),但类型推导逻辑是严格按照元组顺序执行的。如果必须严格保留顺序感知,建议直接使用元组类型而非联合类型,因为联合类型的无序性是TypeScript的核心设计之一。
如果是完全匹配Promise.all的场景,直接返回元组类型更合理:
type RightType<T extends readonly (() => any)[]> = { [K in keyof T]: T[K] extends () => infer R ? R : never; }; type Res1 = RightType<[A, B]>; // ['a', 'b'] type Res2 = RightType<[B, A]>; // ['b', 'a']
内容的提问来源于stack exchange,提问作者Eve-Sama

