使用条件类型缩小从工厂函数返回的实例



下面的代码反映了我试图解决的一个问题。我有一个指定某个对象的接口,它有两个真正的具体实现。一个impl的方法返回A,而另一个的方法则返回B。很明显,在界面上我可以做someMethod(): A | B。我认为如果启用了严格类型,这会很好,但不幸的是,这不是一个选项,在我的情况下,不能启用严格类型。

所以我的想法是,我可以尝试使用条件类型来帮助编译器缩小联合类型的范围。我提供了一些可编译的代码。我在内联中添加了注释,以指定我正在做什么。

interface Str { _kind: 'str'; }
interface Num { _kind: 'num'; }
// Our conditional type say either the return value will be a string or a number
// based on which interface we specify, Num or Str
type NumOrStr<T> = T extends Str 
? string 
: number;
// Interface that clients will use,
// magic method either returns a number or a string
// Here we can (or at least I'm trying to) narrow it down
// by using the conditional type and the generic T arg which
// will be either Num or Str
interface Base<T> {
magic(): NumOrStr<T>;
}
class AString implements Base<Str> {
magic(): NumOrStr<Str> {
return '';
}
}
// We can create instances directly of AString class
const as = new AString();
as.magic(); // and the return value of this is `string`
class ANumber implements Base<Num> {
magic(): NumOrStr<Num> {
return 42;
}
}
const an = new ANumber();
an.magic(); // same thing here, return value is `number`
// Now we can try to hide this behind a factory function which will
// return an appropriate instance based on some parameter the client
// can provide 
function factory<T>(t: 'num' | 'str'): Base<T> {
if (t === 'num') {
return new ANumber() as Base<T>; // I have to do this case otherwise
// I get type errors I don't know how to fix
} else {
return new AString() as Base<T>;
}
}
// But it doesn't really work
const x = factory('str');
const r = x.magic(); // this is a 'number'. But it should be a 'string'!!
const y = factory('num');
const s = x.magic(); // this is also a 'number'

有什么方法可以使factory方法正确地指定magic的返回值吗?

您的NumOrStr是为{_kind: "str"}{_kind: "num"}之类的对象设计的,因此您可以使用它作为类型参数来代替"str""num":

function factory<T extends "num" | "str">(t: T): Base<{_kind: T}> {
if (t === 'num') {
return new ANumber() as Base<{_kind: T}>;
} else {
return new AString() as Base<{_kind: T}>;
}
}
const x = factory('str');
const r = x.magic(); // this is a string
const y = factory('num');
const s = y.magic(); // this is a number

游乐场链接

最新更新