我想要一个接口,我知道参数将是几种不同类型中的一种(来自第三方库),并且根据它的类型,返回类型将会改变,某些类型也来自第三方库,或者是像 void 或 null 这样的基本类型。
这是复制我的问题的最简单的代码
type foo = {
statusCode: number;
body: string;
}
type fooReturn = string; // in reality, this could be a complex type
type bar = {
results: [];
}
type barReturn = number; // in reality, this could be a complex type
interface foobar {
myFunc<T extends (foo | bar)>(event: T): T extends foo ? fooReturn : barReturn;
}
class myClass implements foobar {
// the below code errors
myFunc(event: foo) { // we tell TS that the param is of type foo
return 'hello' // so the return must be a string
}
}
错误是
Property 'myFunc' in type 'myClass' is not assignable to the same property in base type 'foobar'.
Type '(event: foo) => string' is not assignable to type '(event: T) => T extends foo ? string : number'.
Types of parameters 'event' and 'event' are incompatible.
Type 'T' is not assignable to type 'foo'.
Type 'foo | bar' is not assignable to type 'foo'.
Type 'bar' is missing the following properties from type 'foo': statusCode, body
我认为处理此问题的正确方法是使用函数重载。然后,您可以使用类型保护来缩小
event
参数并返回正确的类型。
type Foo = {
statusCode: number;
body: string;
};
type FooReturn = string;
type Bar = {
results: [];
};
type BarReturn = number;
interface FooBar {
myFunc(event: Foo): FooReturn;
myFunc(event: Bar): BarReturn;
myFunc(event: Foo | Bar): FooReturn | BarReturn;
}
class myClass implements FooBar {
myFunc(event: Foo): FooReturn;
myFunc(event: Bar): BarReturn;
myFunc(event: Foo | Bar): FooReturn | BarReturn{
if ("results" in event) {
return 666;
} else {
return "hello";
}
}
}
这里的问题似乎是您错误地范围了您的意图的generic类型参数。类型
interface Foobar {
myFunc<T extends Foo | Bar>(event: T): T extends Foo ? FooReturn : BarReturn;
}
是具有泛型方法的特定类型。泛型方法的泛型类型参数的范围仅限于调用签名,这意味着 caller 决定其中的类型参数。如果我想实现一个
Foobar
,那么它的 myFunc()
方法必须允许调用者使用他们想要的任何 T
进行调用。它必须支持两者myFunc(event: Foo): FooReturn
和myFunc(event: Bar): BarReturn
。但是 MyClass
没有正确实现;它只支持Foo
而不支持Bar
。
相反,您可能想要
interface Foobar<T extends Foo | Bar> {
myFunc(event: T): T extends Foo ? FooReturn : BarReturn;
}
这是一个通用类型,具有特定方法。泛型类型的泛型类型参数的范围仅限于类型本身,这意味着implementer 决定其中的类型参数。根据该定义,您需要决定是实施
Foobar<Foo>
还是 Foobar<Bar>
。一旦做出决定,T
的类型参数就会被选择并固定。 myFunc()
的呼叫签名不是通用的。并且您的 MyClass
确实正确实施了 Foobar<Foo>
:
class MyClass implements Foobar<Foo> {
myFunc(event: Foo) { // okay
return 'hello'
}
}