基于泛型类型的 Vary 函数重载
Vary function overload based on generic type
如何提供基于具有 never
类型的参数之一的函数重载?
type Message<T> = { id: string };
function publish<T>(message: Message<T>, payload: T) {
// ...
}
如果 T
是 never
,这意味着消息永远不会有有效载荷,因此我不希望函数期望 data
参数。
我重载了函数以提供 never
案例的替代签名,这使得 payload
参数可选。
function publish(message: Message<never>, payload?: never): void
function publish<T>(message: Message<T>, payload: T): void {
// ...
}
这适用于 Message<never>
情况,但会中断所有其他调用:
let NeverMessage: Message<never> = { id: "never-message" };
let NumberMessage: Message<number> = { id: "number-message" };
publish(NeverMessage);
// All good!
publish(NumberMessage, 10);
// function publish(message: Message<never>, payload?: undefined): void
// Argument of type '10' is not assignable to parameter of type 'undefined'
如何在不使 payload
在两个签名中都可选的情况下重载定义?
你并不像你想象的那样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">
。为了防止 that 你需要通过函数 属性 启用 Message<T>
invariant in T
, which can be accomplished if you've got --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!
...呃,但我离题了,因为你不是在问这个。
你的主要问题的答案是记得添加一个单独的实现签名。
好的,希望对您有所帮助;祝你好运!
如何提供基于具有 never
类型的参数之一的函数重载?
type Message<T> = { id: string };
function publish<T>(message: Message<T>, payload: T) {
// ...
}
如果 T
是 never
,这意味着消息永远不会有有效载荷,因此我不希望函数期望 data
参数。
我重载了函数以提供 never
案例的替代签名,这使得 payload
参数可选。
function publish(message: Message<never>, payload?: never): void
function publish<T>(message: Message<T>, payload: T): void {
// ...
}
这适用于 Message<never>
情况,但会中断所有其他调用:
let NeverMessage: Message<never> = { id: "never-message" };
let NumberMessage: Message<number> = { id: "number-message" };
publish(NeverMessage);
// All good!
publish(NumberMessage, 10);
// function publish(message: Message<never>, payload?: undefined): void
// Argument of type '10' is not assignable to parameter of type 'undefined'
如何在不使 payload
在两个签名中都可选的情况下重载定义?
你并不像你想象的那样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">
。为了防止 that 你需要通过函数 属性 启用 Message<T>
invariant in T
, which can be accomplished if you've got --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!
...呃,但我离题了,因为你不是在问这个。
你的主要问题的答案是记得添加一个单独的实现签名。
好的,希望对您有所帮助;祝你好运!