Как написать тип возвращаемого значения для метода TypeScript, который либо возвращает заданный ввод, либо член, содержащийся во вводе? - PullRequest
0 голосов
/ 09 мая 2020

Если у меня есть функция, которая будет возвращать либо предоставленное значение (если это не контейнер), либо значение внутри контейнера (если предоставленное значение равно контейнеру), как мне правильно объявить тип возвращаемого значения для этой функции? Например:

interface Container<TValue> {
  value: TValue;
}

declare function isContainer<TContained>(
  object: unknown
): object is Container<TContained>;

type ContainedTypeOrItself<T> = T extends Container<infer U> ? U : T

function getContainedValueOrSelf<T>(
  input: T,
): ContainedTypeOrItself<T> {
  if (isContainer<ContainedTypeOrItself<T>>(input)) {
    return input.value
  }

  return input
}

Этот подход не работает, потому что компилятор TypeScript вызывает ошибку для оператора return input:

Type 'T' is not assignable to type 'ContainedTypeOrItself<T>'.

Поскольку я считаю, что предложение if фильтрует все объекты типа Container, я бы подумал, что когда код дойдет до оператора return input, T не будет Container, и поэтому ContainedTypeOrItself<T> должен быть набран сам по себе, T. Очевидно, мое понимание того, как это должно работать, неверно, учитывая ошибку компилятора.

В моем другом, связанном вопросе я узнал, почему использование тернарного оператора с небрежным использованием any вызвало другое поведение, чем при использовании оператора if-else. Тем не менее, я все еще думаю, что я слишком туп, чтобы понять, что на самом деле не так с кодом, и я читал комментарии и ответы там несколько раз. Они действительно ответили в точности на заданный там вопрос: почему было различное поведение между тернарным оператором и if-else, и за это я благодарен - это было именно то, что я искал. Однако в этом вопросе я использую более простой пример (спасибо!) И стараюсь быть как можно более конкретным c при вводе текста. Мне все еще трудно понять, почему это не работает, даже когда я пытаюсь применить уроки, извлеченные из использования any, тернарного оператора, и используя упрощенный код из этого ответа.

Спасибо за то, что нашли время, чтобы прочитать это, и за вашу помощь!

1 Ответ

1 голос
/ 10 мая 2020

Я думаю, что это чрезмерная инженерия. Заметьте меня, если я неправильно понял, и я удалю свой ответ.

Почему я считаю, что ваш код намного сложнее того, что он пытается решить?
Если ваше намерение состоит в том, чтобы сгладить содержащийся объект, возвращаемый тип плоской функции всегда должен быть raw type, T.
Использование ключевого слова infer усложняет ваше решение, поэтому, если цель - получить плоский объект T, не пытайтесь вернуть какой-либо другой тип, кроме T.

Ваш тип ContainedTypeOrItself<T> будет описан как объединение, например

type ContainedTypeOrItself<T> = Container<T> | T;

Тогда плоская функция будет выглядеть как

const getContainedValueOrSelf = <T>(input: ContainedTypeOrItself<T>): T =>
  isContainer<T>(input)
  ? input.value
  : input;

И да, троичная операция сильно поддерживается, и компилятор прекрасно ее понимает.

Пара строк показывает плоское поведение

const container: Container<number> = { value: 6 };

const decontained: number = getContainedValueOrSelf(container);
const raw: number = getContainedValueOrSelf(6);

Мне кажется подозрительным, что то, на что я пытаюсь ответить, выглядит проще, чем ваш код. Так что предупредите меня, если я не понял ваш вопрос.

...