输入类型select怎么能和输出支持联合呢?

How can the input type select the output and support a union?

我有下面的代码可以接受可迭代或异步可迭代并返回相同类型的对象。它也有一个可以选择柯里化的数字。

function _buffer<T>(size: number, iterable: AsyncIterable<T>): AsyncIterableIterator<T> {
  throw new Error('not important')

}

function* syncBuffer<T>(size: number, iterable: Iterable<T>): IterableIterator<T> {
  throw new Error('not important')
}

export function buffer<T>(
  size: number
): {
  (curriedIterable: AsyncIterable<T>): AsyncIterableIterator<T>
  (curriedIterable: Iterable<T>): IterableIterator<T>
  (curriedIterable: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>
}
export function buffer<T>(size: number, iterable: AsyncIterable<T>): AsyncIterableIterator<T>
export function buffer<T>(size: number, iterable: Iterable<T>): IterableIterator<T>
export function buffer<T>(size: number, iterable: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>
export function buffer<T>(size: number, iterable?: Iterable<T> | AsyncIterable<T>) {
  if (iterable === undefined) {
    return <R>(curriedIterable) => buffer<R>(size, curriedIterable)
  }
  if (iterable[Symbol.asyncIterator]) {
    return _buffer(size, iterable as AsyncIterable<T>)
  }

  return syncBuffer(size, iterable as Iterable<T>)
}


function run(a: AsyncIterable<any>) {
  return buffer(4, a)
}


function run(a: AsyncIterable<any> | Iterable<any>) {
  return buffer(4, a)
  return buffer(4)(a)
}

但是我在编译时遇到以下类型错误。

Overload signature is not compatible with function implementation.ts(2394) 

// in reference to 
export function buffer<T>(size: number, iterable: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>

不过好像不是这样?如果我删除那个重载签名,当我不知道它是哪个时,我就不能用联合调用函数。

TypeScript 中的

Function overloads 将函数签名分为两部分:一个是 call 签名列表,函数的调用者可以看到。这些也只是称为 "overload signatures"。可以有一个或多个。调用签名没有正文。

另一面是 实现 签名,函数的实现 而不是调用者 可以看到。只能有一个实现签名。实现签名必须有一个正文。

调用签名必须在实现签名之前。实现签名必须是 "compatible with" 调用签名(例如,实现签名不能要求任何调用签名都没有提供的参数),但它们不是一回事。


您的问题:您试图将实现签名视为调用签名。

解决方法:在列表末尾添加一个额外的调用签名。可以和实现签名一样:

// call signatures:
function foobar<T>(foo: AsyncIterable<T>): AsyncIterable<T>;
function foobar<T>(foo: Iterable<T>): Iterable<T>;
// add the following call signature
function foobar<T>(foo: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>;

// implementation signature:
function foobar<T>(foo: Iterable<T> | AsyncIterable<T>) {
  return foo
}

希望对您有所帮助。祝你好运!


已更新以处理新表格:

type CurriedBufferResult<T> = {
  (curriedIterable: AsyncIterable<T>): AsyncIterableIterator<T>
  (curriedIterable: Iterable<T>): IterableIterator<T>
  (curriedIterable: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>
};
export function buffer<T>(
  size: number
): CurriedBufferResult<T>;
export function buffer<T>(size: number, iterable: AsyncIterable<T>): AsyncIterableIterator<T>
export function buffer<T>(size: number, iterable: Iterable<T>): IterableIterator<T>
export function buffer<T>(size: number, iterable: Iterable<T> | AsyncIterable<T>): Iterable<T> | AsyncIterable<T>
export function buffer<T>(size: number, iterable?: Iterable<T> | AsyncIterable<T>):
Iterable<T> | AsyncIterable<T> | CurriedBufferResult<T> 
{
  // impl here
  return null!;
}

这与之前的解释相同,但我明确注释了实现签名 return 类型以表明它将包含可能的 return 调用签名类型的意图。这是确保调用签名和实现签名兼容的一部分。

现在由您来确保实现(在 // impl here 中)符合该注释。您可能看到的问题是您的函数实现实际上 return 上面注释的类型,并且推断的实现 return 类型与调用签名不匹配。

祝你好运。

这是一个单独的答案,因为它提供了一种不同的方法。人们有时 运行 遇到重载签名的问题之一是他们 don't always behave intuitively with unions of parameters。这是一个愚蠢的例子:

// call signatures
function foo(x: string): number;
function foo(x: number): string;
// implementation
function foo(x: string | number): number | string {
  return (typeof x === 'string') ? x.length : "a".repeat(x);
}

函数foo()接受一个string和return一个number,或者接受一个number和return一个[=17] =].这按预期工作:

const num: number = foo("string"); // okay
const str: string = foo(12345); // okay

但是人们期望您可以将类型 string | number 的东西传递给它并得到类型 number | string 的值。有时这种期望来自将实现签名与调用签名混淆,其他时候似乎编译器应该能够 select 多个重载并执行它们的统一。但这不会发生。编译器只选择一个重载签名(至少从 TS3.3 开始,无论如何。过去不可能调用函数类型的联合,但是 now you can... well, with caveats。也许最终会发生重载统一):

const oops: string | number = foo(Math.random() < 0.5 ? "string" : 12345); // error!

我在另一个答案中的建议是通过添加一个专门对应联合的调用签名来解决这个问题。这确实有效:

function foo(x: string): number;
function foo(x: number): string;
function foo(x: string | number): number | string; // added
function foo(x: string | number): number | string {
  return (typeof x === 'string') ? x.length : "a".repeat(x);
}

const num: number = foo("string"); // okay
const str: string = foo(12345); // okay
const oops: string | number = foo(Math.random() < 0.5 ? "string" : 12345); // okay

但还有另一种方法。您可以使用 generic functions and conditional types 代替三个调用签名,如下所示:

function foo<X extends string | number>(x: X): X extends string ? number : string;
function foo(x: string | number): number | string {
  return (typeof x === 'string') ? x.length : "a".repeat(x);
}

const num: number = foo("string"); // okay
const str: string = foo(12345); // okay
const oops: string | number = foo(Math.random() < 0.5 ? "string" : 12345); // okay

为什么这样行得通?

嗯,根据传入的参数,泛型X将被推断为string | number的任何子类型。对于 foo("string")X 被推断为 string literal type "string". For foo(12345), X is inferred as the numeric literal 类型 12345。在 Math.random() 的调用中,X 被推断为 "string" | 12345。所以所有调用都应该成功。

他们return做什么?这就是条件类型的用武之地。类型 X extends string ? number : string 意味着如果 Xstring 的子类型,则条件类型将为 number。否则条件类型将为 string。所以对于foo("string")X extends string为真,return类型为number。对于foo(12345)X extends string为false,return类型为string。那么 Math.random() 的联合类型呢?好吧,因为条件类型 distribute over unions,它最终变成了 number | string

您可能想也可能不想对您的函数做类似的事情:

type MaybeIterable<T> = AsyncIterable<T> | Iterable<T>;
type UnmaybeIterable<M extends MaybeIterable<any>> = M extends Iterable<infer T> ? Iterable<T> : M extends AsyncIterable<infer T> ? AsyncIterable<T> : never;
type CurriedBufferResult = {
  <M extends MaybeIterable<any>>(curriedIterable: M): UnmaybeIterable<M>
 };
export function buffer(
  size: number
): CurriedBufferResult;
export function buffer<M extends MaybeIterable<any>>(size: number, iterable: M): UnmaybeIterable<M>;
export function buffer(size: number, iterable?: MaybeIterable<any>): CurriedBufferResult | UnmaybeIterable<any>
{
  // impl here
  return null!;
}

这是你想要的吗?不确定。