Ошибка ограничения типа подтипа универсальной функции и путаница - PullRequest
1 голос
/ 03 октября 2019

Я играл с идеей простой функции-оболочки, использующей TypeScript. Я закончил тем, что получил что-то, работающее со следующим:

export function logFn<T extends (...args: any[]) => any>(
  fn: T,
): (...args: Parameters<T>) => ReturnType<T>  {
  const log = (...args: Parameters<T>): ReturnType<T> => {
    console.log('log')
    return fn(...args)
  }
  return log
}

Это работает, и компилятор счастлив. Мой вопрос касается моей первоначальной попытки, которая выглядела примерно так:

export function logFn<T extends (...args: any[]) => any>(
  fn: T,
): T  {
  const log: T = (...args) => {
    console.log('log')
    return fn(...args)
  }
  return log
}

Это приводило меня к ошибке при объявлении переменной log с ошибкой:

Type '(...args: any[]) => any' is not assignable to type 'T'.
  '(...args: any[]) => any' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '(...args: any[]) => any'.

Кажется, чтоесть какое-то ограничение отношения подтипа, которое я не могу полностью обернуть вокруг себя (эта ошибка также не делает мне слишком много одолжений). Я надеялся, что кто-то с лучшим умственным пониманием этого материала может дать мне достойное объяснение, чтобы я мог быть менее смущен этим поведением (что, я уверен, правильно).

1 Ответ

2 голосов
/ 03 октября 2019

«Проблема» заключается в следующем: T extends (...args: any[]) => any

Это более наглядно, если вы видите следующую версию:

export function logFn<T extends (...args: any[]) => any>(fn: T): T {
  return (a, b) => fn(a, b);
}

Вы увидите, что ошибка удерживается

Тип '(a: любой, b: любой) => любой "нельзя назначить типу" T ". '(a: any, b: any) => any' присваивается ограничению типа 'T', но экземпляр T может быть создан с другим подтипом ограничения '(... args: any []) => any '.

Оба из приведенных ниже действительны до T extends (...args: any[]) => any

  1. logFn((a, b) => a + b)
  2. logFn((a, b, c) => c)

Но если вы вернетесь к приведенному мною примеру, внутреннее определение будет выглядеть так:

return (a, b) => fn(a, b);

Так что вариант 2. выдаст здесь ошибку, поэтому машинопись предупреждает вас об этом.

logFn<T extends (...args: any[]) => any>(fn: T): T

Мы собираемся получить тип T и вернуть тип T. return (a, b) => fn(a, b); является допустимым типом возвращаемого значения, оно распространяется (...args: any[]) => any, но как вы можете быть уверены, что значение, переданное в fn (T), соответствует этой подписи? (т. е. это может быть другой несовместимый подтип (...args: any[]) => any)

Не уверен, что я объяснил достаточно хорошо, но это мое понимание

Причина, по которой вы можете обойтись, заключается в добавлении (...args: Parameters<T>) => ReturnType<T>вы говорите компилятору, что параметры и возвращаемые типы должны совпадать с типами передаваемой функции, в отличие от T, который может быть любым другим определением функции

...