TypeScript: тип «XXX» нельзя присвоить типу «XXX|YYY».

Я пишу функцию высшего порядка, opposite(fn). Этот opposite принимает логическую или числовую fn функцию в качестве входных данных и возвращает новую функцию, которая должна быть того же типа, что и fn. Если исходная функция имеет тип, возвращающий логическое значение, новая функция возвращает «не то логическое значение». Если исходная функция имеет тип, возвращающий число, новая функция возвращает 1 или 0 в зависимости от того, был ли результат нулевым или нет. Вы можете использовать opposite, чтобы инвертировать фильтр с someArray.filter(testCondition) на someArray.filter(opposite(testCondition)).

Проблема: оба return предложения выдают ошибку Type 'number' is not assignable to type 'ReturnType<T>' ts(2322).

Почему я не могу присвоить boolean или numberboolean|number? Как мне применять типы данных к моей функции opposite?

const opposite =

  < T extends
      | ((...args: any[]) => boolean)
      | ((...args: any[]) => number)
  >(fn: T) =>

  (...args: Parameters<T>): ReturnType<T> => {

    const result = fn(...args);
    if (typeof result === "number") {
      return result === 0 ? 1 : 0;
    } else {
      return !result;
    }
  };
Шаблоны Angular PrimeNg
Шаблоны Angular PrimeNg
Как привнести проверку типов в наши шаблоны Angular, использующие компоненты библиотеки PrimeNg, и настроить их отображение с помощью встроенной...
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Освоение принципов SOLID в JavaScript: Пошаговое руководство
Принцип единой ответственности (SRP): класс должен иметь только одну причину для изменения. Другими словами, у него должна быть только одна...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
0
0
102
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я спотыкаюсь о такие вещи каждый раз, когда снова беру в руки ТС.

Я не думаю, что вы можете ввести это без частичного отключения проверки типов тем или иным образом.

Обратите внимание, что я добавил утилиту Widen, потому что ваша подпись функции была неправильной: если fn — это () => true as const, то opposite(fn) — это () => true вместо () => boolean

type Widen<T> = T extends number ? number : T extends boolean ? boolean : T;

const opposite =

  < T extends
      | ((...args: any[]) => boolean)
      | ((...args: any[]) => number)
  >(fn: T) =>

  (...args: Parameters<T>): Widen<ReturnType<T>> => {

    const result = fn(...args);
    if (typeof result === "number") {
      return result === 0 ? 1 : 0 as any;
    //                            ------
    } else {
      return !result as any;
    //               ------
    }
  };
const opposite:{
    <Args extends unknown[]>(f: (...args: Args) => boolean): (...args: Args) => boolean
    <Args extends unknown[]>(f: (...args: Args) => number): (...args: Args) => 0 | 1
} = <Args extends unknown[]>(f: (...args: Args) => boolean | number) => (...args: Args): any => {
//                                                                                       ---
    const result = f(...args);

    if(typeof result === 'number') {
        return result === 0 ? 1 : 0;
    } else {
        return !result;
    }
};
type Widen<T> = T extends number ? number : T extends boolean ? boolean : T;

const opposite =
    <Args extends unknown[], R extends boolean | number>
        (f: (...args: Args) => R) => (...args: Args): Widen<R> => {
            const result = f(...args);

            if(typeof result === 'number') {
                return result === 0 ? 1 : 0 as any;
                //                          ------ 
            } else {
                return !result as any;
                //             ------
            }
        };

Несколько вещей могут привести к тому, что он не скомпилируется:

  • В вашем случае я думаю, что виновником является то, что ReturnType<T> не определено: это не number | boolean, и компилятор не вычислит значение.
  • В случае реализации с перегрузками проблема в том, что перегрузки — это пересечение, а пересечение boolean & number — это never.
  • Проблема с последней реализацией заключается в том, что R может быть предоставлен вызывающей стороной: если они передают 5, boolean больше не расширяется R.

Я могу немного ошибаться в этих интерпретациях, но эта ментальная модель работает довольно хорошо, и суть в том, что вы застряли.

Основываясь на ответе Джеффри, я сократил и исправил свой код. Мне не очень нравилась конструкция Widen<T>; по сути, он говорит: «Если T возвращает число, то число; иначе, если T возвращает логическое значение, логическое значение; в противном случае, что бы ни возвращал T» - для меня это было похоже на то, чтобы сказать «все, что возвращает T». Мы можем обойтись без Widen; настоящим ключом были изменения, которые выделил Джеффри, переход от return ... as number к return ... as any. Это интересно; вместо того, чтобы сообщать TypeScript тип ответа, мы должны позволить ему понять это самому!

Federico Kereki 19.11.2022 10:00

Будьте осторожны, использование any не позволяет TS делать какие-либо выводы: any расширяет любой тип, а любой тип расширяет any, поэтому его использование фактически отключает проверку типов. В этом случае тип возвращаемого значения и ограничения типа полезны для вызывающих, но реализация не проверяет тип, поэтому как разработчик вы должны быть осторожны, чтобы выполнить свой контракт.

geoffrey 19.11.2022 11:48

что касается Widen, взгляните на эту игровую площадку: typescriptlang.org/play?#code/… Вы не можете вернуть ReturnType<T>, потому что тип возврата может быть слишком узким

geoffrey 19.11.2022 11:55

О том, как пишется Widen: T extends number ? number : ... означает «если T есть 5, вернуть number вместо 5». opposite(() => 5) не должен быть типа () => 5, а () => number или () => 0, если хотите быть более точным

geoffrey 19.11.2022 12:02

Другие вопросы по теме