Это связано с тем, что интерфейс ITestDispatch
означает, что функция может выполнять действие любого подтипа A
, поэтому он конфликтует с объявлением типов и возвращаемым типом функции TestDispatch1Func
, которая ограничена MyAnyAnyAction
, Интерфейс принимает любой подтип A, в то время как реализация принимает только 1 подтип.
Например, если у вас был другой интерфейс
export interface AnotherAction extends MyAnyAction{}
Определение ITestDispatch1<MyAnyAction>
позволяет вам вызывать TestDispatch1Func(action: AnotherAction)
начиная с AnotherAction extends MyAnyAction
, но это, очевидно, будет противоречить определению функции, ожидающему только MyAnyAnyAction
.
Здесь есть 3 решения
export interface MyAction<T = any> {
type: T
}
export interface MyAnyAction extends MyAction {
[extraProps: string]: any
}
export interface MyAnyAnyAction extends MyAnyAction{}
// solution 1: specify the action input & output types in function defnition
export interface ITestDispatch1<T extends MyAction = MyAnyAction> {
(action: T): T
}
// this means that the function will always be called with MyAnyAnyAction and return MyAnyAnyAction
const TestDispatchFunc1: ITestDispatch1<MyAnyAnyAction> = (action) => {
// here you can always return `MyAnyAnyAction`,
// because you explicitly declared it the as function output type
let obj: MyAnyAnyAction = {
type: 'skdw',
da: 20
};
return obj;
}
// solution 2: separate action input & output types, specify output type in function defintion
export interface ITestDispatch2<A extends MyAction, R extends A> {
<T extends A>(action: T): R
}
// this function can be called with any subtype of MyAnyAction, but will always return MyAnyAnyAction
const TestDispatchFunc2: ITestDispatch2<MyAnyAction, MyAnyAnyAction> = (action) => {
// here you can always return `MyAnyAnyAction`,
// because you explicitly declared it the as function output type
let obj: MyAnyAnyAction = {
type: 'skdw',
da: 20
};
return action;
}
// solution 3: decide the function input & output types types in function invocation
export interface ITestDispatch3<A extends MyAction = MyAnyAction> {
<T extends A>(action: T): T
}
// this function can be called with any subtype of MyAnyAction, returns the same subtype
const TestDispatchFunc3: ITestDispatch3<MyAnyAction> = (action) => {
// can't return MyAnyAnyAction because the return type is the same as the input type,
// which can be any subtype of MyAnyAction, not necessarily MyAnyAnyAction
// let obj: MyAnyAnyAction = {
// type: 'skdw',
// da: 30
// };
return action;
}
// the result type is determined base on the input type
const result1 = TestDispatchFunc3({} as MyAnyAnyAction) // result: MyAnyAnyAction
const result2 = TestDispatchFunc3({} as MyAnyAction) // result: MyAnyAction
TS Playground здесь