Typescript › Универсальные шаблоны › Union Constraint

Почему компилятор машинописного текста выдает следующую ошибку:

Operator '+' cannot be applied to types 'T' and 'T'.,

при компиляции:

export const addNumbersOrCombineStrings = <T extends string | number>(
  param1: T,
  param2: T
): T => param1 + param2

?


person RichardForrester    schedule 04.05.2019    source источник


Ответы (1)


Соответствующая проблема в GitHub — Microsoft/TypeScript#12410. Ваш конкретный вопрос рассмотрен в комментарии от Райан Кавано:

T + T где T расширяет string | number по-прежнему запрещено - мы не считаем это хорошим вариантом использования, потому что невозможно предсказать, получите ли вы конкатенацию или добавление, поэтому люди не должны этого делать.

То же самое произойдет с вами, если вы просто попытаетесь сложить вместе два типа string | number:

declare const x: string | number;
x + x; // error!
// Operator '+' cannot be applied to types 'string | number' and 'string | number'.

Так что я думаю, они серьезно, что они не хотят, чтобы вы делали это. Вы всегда можете настроить компилятор по своему усмотрению и отключить проверку типов с помощью утверждение типа;

export const addNumbersOrCombineStrings = <T extends string | number>(
  param1: T,
  param2: T
): T => (param1 as any) + param2;

Но вы не хотите этого делать, тем более что T extends string | number будет выводить строка или number литерал, который не дает ожидаемого поведения:

const notThree = addNumbersOrCombineStrings(1, 2);
// const notThree: 1 | 2

const notHi = addNumbersOrCombineStrings("h", "i");
// const notHi: "h" | "i"

К сожалению, эти результаты являются объединениями литералов. И чтобы исправить это с помощью дженериков, вы, вероятно, захотите начать использовать причудливые условные типы для расширения литералов:

type SN<T extends string | number> = (T extends string ? string : never) | 
  (T extends number ? number : never);

export const addNumbersOrCombineStrings = <T extends string | number>(
  param1: T,
  param2: SN<T>
): SN<T> =>
  (param1 as any) + param2;

const n = addNumbersOrCombineStrings(1, 2); // const n : number;
const s = addNumbersOrCombineStrings("h","i"); // const s: string;

// and do you even want to support this:
const sn = addNumbersOrCombineStrings(
  Math.random() < 0.5 ? "a" : 1,
  Math.random() < 0.5 ? "b" : 2
); // const sn: string | number;

Но, вероятно, там тоже есть пограничные случаи (например, когда переданное значение на самом деле string | number, как в случае sn выше). Я начинаю понимать, почему они не хотят поддерживать сложение значений типа string | number вместе. Во всяком случае, надеюсь, что это поможет. Удачи!

person jcalz    schedule 05.05.2019