您并不是真正的overloading 功能,就像您认为的那样。重载函数具有 调用签名 的有序列表,这些调用签名对函数的调用者可见并且没有实现(它们以 ; 结尾而不是 {...}),以及单个 实现签名对函数的实现可见。您表面上希望调用者而不只是实现看到publish<T>(message: Message<T>, payload: T): void 签名。如果是这样,您需要执行以下操作:
// call signatures
function publish(message: Message<never>, payload?: never): void;
function publish<T>(message: Message<T>, payload: T): void;
// implementation signature
function publish<T>(message: Message<T>, payload: T): void {
// ...
}
这应该可以解决您的问题。
顺便说一句,请注意 discouraged 拥有像这样的泛型类型
type Message<T> = { id: string };
类型参数未使用的地方。 TypeScript 的类型系统主要是structural 而不是名义上的,这意味着如果两个类型具有相同的结构,那么它们是相同的类型,即使您使用不同的名称来引用它们。在这种情况下,Message<never> 和Message<number> 都是{ id: string },因此它们是相同的类型。
编译器可能会将显式键入为Message<never> 的表达式与显式键入为Message<number> 的表达式区别对待,并且您的重载将按照您想要的方式运行。但不能保证这将永远有效,weird stuff 可能会在它不起作用时发生。
这里的传统观点是在类型结构的某处使用类型参数。甚至像
type Message<T> = { id: string; __messageType?: T };
有时足以让事情正常运行,即使在运行时不需要任何 __messagetype 属性。
您可能还需要注意子类型和超类型,因为即使上面的Message<T> 定义有Message<never> 是任何Message<T> 的子类型,这意味着您可以毫无错误地调用publish(NeverMessage, "hello there");。值NeverMessage 将被视为有效的Message<"hello there">。为了防止,您需要创建Message<T> invariant in T,如果您通过这样的函数属性启用了--strictFunctionTypes,则可以完成此操作:
type Message<T> = { id: string; __messageType?: (x: T) => T };
您还需要扩展实现签名:
function publish(message: Message<never>, payload?: never): void;
function publish<T>(message: Message<T>, payload: T): void;
function publish<T>(message: Message<T> | Message<never>, payload?: T): void {
// ...
}
这会导致
publish(NeverMessage); // okay
publish(NumberMessage, 10); // okay
publish(NeverMessage, "hello there"); // error!
...呃,但我离题了,因为你没有问这个。 ?
您的主要问题的答案是记住添加单独的实现签名。
好的,希望对您有所帮助;祝你好运!
Link to code