主要问题在export type ActionReducerX<T> = () => T;。
似乎Map2<WithBase<T>> 和Map2<T> & Map2<Base> 可以相互分配:
declare var returnType: Map2<WithBase<Value>>
declare var returnValue: Map2<Value> & Map2<Base>
returnType = returnValue
returnValue = returnType
但当我们将它们与函数参数类型一起使用时,情况并非如此。
export function mergeAll2<T>(t: Map2<T>): Map2<WithBase<T>> {
const b: Map2<Base> = {
a: () => ({ aa: '' })
};
let x: Map2<T> & Map2<Base> = null as any as Map2<T> & Map2<Base>;
let y: Map2<WithBase<T>> = null as any as Map2<WithBase<T>>
x = y // ok
y = x // error
const result = {
...t,
...b
};
return result // error
}
由于函数参数是逆变的,我们不再允许将x 分配给y(Map2<T> & Map2<Base> 分配给Map2<WithBase<T>>)、The arrow of inheritance points in the opposite direction。
有一个解决方法:
export type ActionReducerX<T> = <U extends T>() => U;
export type Map2<T> = {
[key in keyof T]: ActionReducerX<T[key]>;
};
interface Base {
a: { aa: string };
}
type WithBase<T> = T & Base;
export function mergeAll2<T>(t: Map2<T>): Map2<WithBase<T>> {
// remove explicit type
const b = {
a: () => ({ aa: '' })
};
return {
...t,
...b
}
}
我们可以从一开始就使这个函数contravariant。
export type ActionReducerX<T> = <U extends T>() => U;
type Animal = {
name: string
}
type Dog = {
paws: 4
} & Animal
type Check = Animal extends Dog ? true : false // false
type Check2 = ActionReducerX<Animal> extends ActionReducerX<Dog> ? true : false // true
尝试删除多余的U 泛型参数,您会看到继承行为将朝相反的方向改变。
我对任何类型的批评持开放态度,因为这个话题(差异)对我来说仍然很难。
Here、here 和 here 您可以找到有关此主题的更多信息
更新ActionReducer的自定义包装器
遗憾的是,我无法扩展 ActionReducer
您可以创建自己的自定义类型包装器。见Wrapper
export type ActionReducerX<T> = () => T;
type Wrapper<T extends (...args: any) => any> = <U extends ReturnType<T>>() => U
export type Map2<T> = {
[key in keyof T]: Wrapper<ActionReducerX<T[key]>>;
};
interface Base {
a: { aa: string };
}
type WithBase<T> = T & Base;
export function mergeAll2<T>(t: Map2<T>): Map2<WithBase<T>> {
// remove explicit type
const b = {
a: () => ({ aa: '' })
};
return {
...t,
...b
}
}
type Animal = {
name: string
}
type Dog = {
paws: 4
} & Animal
type Check = Animal extends Dog ? true : false // false
type Check2 = Wrapper<ActionReducerX<Animal>> extends Wrapper<ActionReducerX<Dog>> ? true : false // true
Playground