Машинопись родовая c и расширение - PullRequest
0 голосов
/ 28 января 2020

Я новичок в Typescript.

У меня есть следующие 4 интерфейса:

export interface MyAction<T = any> {
  type: T
}

export interface MyAnyAction extends MyAction {
  [extraProps: string]: any
}

export interface MyAnyAnyAction extends MyAnyAction{

}

export interface ITestDispatch<A extends MyAction = MyAnyAction> {
  <T extends A>(action: T): T
}

Я хочу создать функцию типа "ITestDispatch".

Я мог бы не понимаю, почему компилятор TS выдает ошибку для следующей функции:

const TestDispatch1Func: ITestDispatch1<MyAnyAction> = (action: MyAnyAnyAction): MyAnyAnyAction => {


  let obj: MyAnyAnyAction = { 
        type: 'skdw',
        da: 20
  };

  return obj;
}

Я получаю приведенную ниже ошибку в "TestDispatch1Fun c":

Type '(action: MyAnyAnyAction) => MyAnyAnyAction' is not assignable to type 'ITestDispatch<MyAnyAction>'. Type 'MyAnyAnyAction' is not assignable to type 'T'. 'MyAnyAnyAction' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'MyAnyAction'.

Спасибо за прояснение моих сомнений.

Ответы [ 2 ]

2 голосов
/ 28 января 2020

Это фактически неясная ошибка в сигнатуре вашей функции. <MyAnyAction> объявляет новый параметр типа, который меняет значение MyAnyAction. Это трудно определить, потому что параметр типа имеет то же имя, что и интерфейс в вашем коде.

const TestDispatch1Func: ITestDispatch<MyAnyAction> =
    <MyAnyAction>(action: MyAnyAction): MyAnyAction => {

Это должно быть

const TestDispatch1Func: ITestDispatch<MyAnyAction> =
    (action: MyAnyAction): MyAnyAction => {

Для немного большего контекста вы можете переименовать <MyAnyAction> на что угодно, поскольку это параметр типа, и в этом контексте это означает создание параметра типа с именем MyAnyAction. Если вы переименуете это, то ошибка также более ясна:

const TestDispatch1Func: ITestDispatch<MyAnyAction> = <T>(action: T): T => {

Тип 'MyAnyAction' не может быть назначен типу 'T'. «MyAnyAction» присваивается ограничению типа «T», но экземпляр «T» может быть создан с другим подтипом ограничения «{}»

1 голос
/ 31 января 2020

Это связано с тем, что интерфейс 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 здесь

...