TypeScript 使用"可管道化"风格的联合类型错误地Assert参数

qnakjoqk  于 4个月前  发布在  TypeScript
关注(0)|答案(7)|浏览(51)

🔎 搜索词

expected zero arguments
pipe
RxJS

🕗 版本与回归信息

  • 这是我尝试的每个版本中的行为

⏯ Playground链接

https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbziAhmANHCAzOBfObKCEOAcigA8ArAZzIG4AoJgYwgDtb5aIBXKKwCmcALxwAsihgALAHRQUHACYkAFAEo4APjgAGOQFY4AfizY1HPgBtrWgFzm1fFUOzAOQ5RuZtO3OCghWht4cV4BYTkwYDAhNVQwNU0xXTIZYDINHyA

💻 代码

import { map, of } from 'rxjs';

const source = Math.random() > 0.5 ? of(null) : of(undefined);

const result = source.pipe(map(() => 'hi'));

🙁 实际行为

对于 pipe 方法,代码报错 "Expected 0 arguments"。

🙂 预期行为

要么:
A. 不报错并推断类型。
或者
B. 报错,但给出一些提示人们如何修复的信息。

关于问题的额外信息

我理解这里可能存在限制,但如果错误能指向正确的方向就更好了。目前的错误令人困惑。
对于在这里寻找解决方法的人:

import { map, of } from 'rxjs';

const source: Observable<null | undefined> = Math.random() > 0.5 ? of(null) : of(undefined);

const result = source.pipe(map(() => 'hi'));
vyu0f0g1

vyu0f0g11#

这个版本不需要像rxjs这样复杂强大的东西来演示问题,它是什么?

ycl3bljg

ycl3bljg2#

你好,@RyanCavanaugh
一个简单的例子是在 #56712 中。问题是,在 Type<A> | Type<B> 中,类型为 Type<T> 的对象没有被推断出具有方法 methodX<A | B>
在 Array 中类似的问题已经在 Easier Method Usage for Unions of Arrays 中得到了解决。

9udxz4iz

9udxz4iz3#

你好,@RyanCavanaugh
一个简单的例子是在 #56712 中。问题在于 Type<A> | Type<B> ,其中类型为 Type<T> 的对象具有方法 methodX<T> ,但没有被推断出具有方法 methodX<A | B>
在 Array 中类似的问题已经在 Easier Method Usage for Unions of Arrays 中得到解决。
那里的 bug 是方法没有被正确评估。
如果 method(null as any as Type<A>) 是有效的,并且 method(null as any as Type<B>) 也是有效的,那么它应该通过。
返回类型是这两个方法调用的联合。

fgw7neuy

fgw7neuy4#

这个版本不需要像rxjs这样复杂强大的东西来演示问题,对吗?
🤣
公平。但是@RyanCavanaugh,如果我必须处理我维护的库(免费)中人们做的愚蠢的事情,我认为你必须处理我用你维护的语言(收费)中我做的愚蠢的事情。你想让这件事变得容易吗? 😆
说实话,我唯一真正关心的这里结果是,至少我可以给人们一个错误,引导他们做正确的事情。否则,如果它在未来的某个TypeScript版本中“正常工作”,那就更好了。

ie3xauqp

ie3xauqp5#

一个小的复制品:

type OperatorFunction<T, R> = (source: Observable<T>) => Observable<R>;
interface Observer<T> { next(value: T): void; }
declare class Observable<T> {
  pipe(): Observable<T>;
  pipe<A>(op: OperatorFunction<T, A>): Observable<A>;
  subscribe(observer: Observer<T>): void;
}
declare const y: Observable<number> | Observable<string>;
y.pipe(); // only the first overload available

可以通过多种方式"固定":

  • 使观察者非泛型
  • 使重载非泛型
  • 使重载更简单( pipe<A>(op: A): Observable<A> )

在尝试创建复制品时还发现了一些奇怪之处:

  • 在某些情况下,没有参数的pipe()返回联合的第一个类型(因此交换联合成员会改变返回类型)
  • 将第二个重载更改为 pipe(op: OperatorFunction<T, number>): Observable<number> 实际上显示了3个重载而不是2个(这有点棒?)
ocebsuys

ocebsuys6#

我可能遗漏了一些东西,但如果你在Observable构造函数内部分支,而不是在其外部,这个问题似乎是可以避免的:

import { type Observable, map, of } from 'rxjs';

const source = of(Math.random() > 0.5 ? "hey" as const : "ho" as const)

const id
  : <type>(x: type) => type
  = (x) => x

const result = source.pipe(id)
//    ^? const result: Observable<"hey" | "ho">

编辑:使示例更清晰
https://tsplay.dev/wgGMbw
https://tsplay.dev/WGGjJW

h79rfbju

h79rfbju7#

next中移除T类型的参数,并出现一个不同的错误:

type Observable<T> = {
  pipe(): Observable<T>;
  pipe<A>(op: (source: Observable<T>) => Observable<A>): Observable<A>;
  subscribe (observer: { next(value: T): void; } ): void;
}
declare const y: Observable<number>|Observable<string>;
const ry1 = y.pipe(); // only the first overload available
// const ry1: Observable<number> | Observable<string> (correct)
const ry2 = y.pipe(0 as any as <T>(source:T)=>T ); // only the first overload available
// error

type Observable2<T> = {
  pipe(): Observable2<T>;
  pipe<A>(op: (source: Observable2<T>) => Observable2<A>): Observable2<A>;
  subscribe (observer: { next(/*value: T*/): void; } ): void;
}
declare const z: Observable2<number>|Observable2<string>;
const rz1 = z.pipe(); // only the first overload available
rz1; //  Observable2<number> (wrong)
const rz2 = z.pipe(0 as any as <T>(source:T)=>T ); // only the first overload available
rz2; //  Observable2<number> (wrong)

相关问题