我有以下代码,它们可以采用可迭代或异步可迭代,并返回相同类型的对象。它也有一个可以选择管理的数字。
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>
但是似乎并非如此吗?如果我删除了该重载签名,则在不知道是哪个联合的情况下,就无法调用该函数。
答案 0 :(得分:1)
功能overloads将功能签名分为两面:一是 call 签名列表,由函数调用者看到。这些也称为“过载签名”。其中可以有一个或多个。呼叫签名没有内容。
另一面是实现签名,通过功能的实现而不是调用方看到。只能有一个实现签名。实现签名必须具有主体。
调用签名必须在实现签名之前。实现签名必须与调用签名“兼容”(例如,实现签名不能要求任何调用签名都不提供的参数),但是它们不是同一个人。
您的问题:您正在尝试将实现签名视为呼叫签名。
解决方法:在列表的末尾添加一个附加的呼叫签名。它可以与实现签名相同:
// 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!;
}
这与以前的解释相同,但是我已经明确注释了实现签名返回类型,以表明其将包含调用签名的可能返回类型的意图。这是确保呼叫签名和实现签名兼容的一部分。
现在由您来确保实现(在// impl here
中)符合该注释。您可能看到的问题是您的函数实现实际上没有返回上面注释的类型,并且推断的实现返回类型与调用签名不匹配。
再次祝你好运。
答案 1 :(得分:1)
这是一个单独的答案,因为它提出了不同的方法。人们有时会遇到过载签名的问题之一是他们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
并返回number
,或者接受number
并返回string
。并按预期工作:
const num: number = foo("string"); // okay
const str: string = foo(12345); // okay
但是人们希望您可以将其传递为string | number
类型,并获得number | string
类型的值。有时,这种期望来自于将实现签名与调用签名相混淆,而其他时候,似乎编译器应该能够选择多个重载并对其进行统一。但这不会发生。编译器仅选择一个重载签名(无论如何至少从TS3.3开始。过去,调用函数类型的并集是不可能的,但是now you can ...好,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和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类型"string"
。对于foo(12345)
,推断X
为numeric literal类型12345
。在与Math.random()
的通话中,X
被推断为"string" | 12345
。因此,所有呼叫都应该成功。
他们还返回什么?这就是条件类型的来源。类型X extends string ? number : string
意味着如果X
是string
的子类型,那么条件类型将是number
。否则,条件类型将为string
。因此,对于foo("string")
,X extends string
为true,返回类型为number
。对于foo(12345)
,X extends string
为false,返回类型为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!;
}
这就是您想要的吗?不确定。