我有2个版本的数据模型(分别称为A
和B
),并且我希望能够在这些实例的实例上执行函数,而不管它们是A还是B。这些函数本身特定于类型A和B。
我想出了解决此问题的方法,但是它们都涉及到声明一个通用类型(即Functions<T>
),这对于我当前的设置是不容易做到的。
type A = {
a: string;
}
type B = {
b: string;
}
type AFunctions = {
make: () => A;
do: (obj: A) => void;
}
type BFunctions = {
make: () => B;
do: (obj: B) => void;
}
type Bundle<
Fns extends AFunctions | BFunctions,
T extends ReturnType<Fns['make']> = ReturnType<Fns['make']>
> = {
obj: T,
fns: Fns,
}
function doIt<M extends AFunctions | BFunctions>(bundle: Bundle<M>) {
bundle.fns.do(bundle.obj);
}
在bundle.fns.do(bundle.obj);
行上,出现打字错误:Argument of type 'ReturnType<M["make"]>' is not assignable to parameter of type 'A & B'
我希望doIt
是类型安全的,因为bundle.obj
与bundle.fns.do
上的参数类型相同。怎么了,这里?有没有解决方法,而无需引入通用的Functions<T>
?
我还可以通过将{ type: 'a' }
和{ type: 'b' }
参数分别添加到Bundle
中,然后进行检查来解决此问题:
if (bundle.type === 'a') {
bundle.fns.do(bundle.obj);
} else if (bundle.type === 'b') {
bundle.fns.do(bundle.obj);
}
但是这种冗余并不理想。
我认为这与缩小通用类型的问题有关:https://github.com/microsoft/TypeScript/issues/17859
答案 0 :(得分:2)
我认为编译器可以抱怨。请考虑以下内容:
let
func : AFunctions | BFunctions = {
'make' : function() : A { return {'a': "A"} },
'do' : function(_ : A) { }
},
someB : B = { 'b' : "B" },
bundle : Bundle<AFunctions | BFunctions> = {
'obj' : someB,
'fns' : func,
}
此类型检查,但是显然'do'
不能与参数'obj'
一起调用。基本问题是,在bundle
中,根据T
的类型,推断类型A | B
是A
,而不是B
或make
,正如我想的那样。
目前尚不清楚您想要实现什么。特别是,尚不清楚为什么不能声明泛型,因为这似乎正是您所需要的:
type GenericBundle<X> = { obj : X, do : (obj : X) => void };
type AFunctions = GenericBundle<A>;
type BFunctions = GenericBundle<B>;
type Bundle = AFunctions | BFunctions;
function doIt<X>(bundle: GenericBundle<X>) {
bundle.do(bundle.obj);
}
let
someA : A = { 'a' : "A" },
someB : B = { 'b' : "B" },
bundle1 : Bundle = {
'obj' : someA,
'do' : function(_ : A) { },
},
bundle2 : Bundle = {
'obj' : someB,
'do' : function(_ : B) { },
},
bundle3_wrong : Bundle = { // doesn't typecheck
'obj' : someA,
'do' : function(_ : B) { },
},
bundle4_wrong : Bundle = { // doesn't typecheck
'obj' : someB,
'do' : function(_ : A) { },
};
doIt(bundle1);
doIt(bundle2);
答案 1 :(得分:0)
不确定这是否可以满足您的用例,但也许会有所帮助-playground link
这里的缺点是do :: obj参数上的“ any”(可以是“ T | any”,也可以只是“ any”),对您而言可能无关紧要。