Олег
12 мая 2021 г., 19:04

Дженерики TypeScript

Эксперементирую с дженериками, пытаюсь понять принцип работы.


interface MyArray<T> { reduce<T>(fn: (arg:T) => T): T; } let arr = [1, 2, 3, 4, 5]; let results = arr.reduce((sum, current) => sum + current, 0); results
В таком виде вроде все работает нормально, но если в массиве поменять одно значение на строку.
interface MyArray<T> { reduce<T>(fn: (arg:T) => T): T; } let arr = [1, 2, 3, 4, '5']; let results = arr.reduce((sum, current) => sum + current, 0); results
Здесь sum + current появляется ошибка:
Operator '+' cannot be applied to types 'string | number' and 'string | number'

Пытался менять описание примерно так и еще несколько вариантов пробовал:
interface MyArray<T> { reduce<T, U>(fn: (arg1:T, arg2:U) => [T,U]): [T,U]; } let arr = [1, 2, 3, 4, '5']; let results = arr.reduce((sum, current) => sum + current, 0); results
Но ничего не помогает и естественно даже не уверен, что правильно делаю. Как все-таки в таком случае сделать описание дженериками в интерфейсе?

Еще вариант
type MyArray<T> = { [N in (number | string)]: T } & { reduce<T>(fn: (arg: T) => T): T } let arr = [1, 2, 3, 4, '5']; let results = arr.reduce((sum, current) => sum + current, 0); results

Здесь, думаю, бага тайпскрипта. С одной стороны логично, что он тебе говорит, что "Нельзя строку или число сложить со строкой или числом" (потому что складывать надо число с числом). С другой стороны TS вполне кушает такое:
const a: string | number = 1; const b: string | number = '2'; const z = a + b;
Он понимает, что z на выходе будет только строка (потому что число плюс строка на выходе строка).

При этом даже так работает:
interface MyArray<T> { reduce<T>(fn: (arg:T) => T): T; } let arr = [1, 2, 3, 4, '5']; let results = arr.reduce((sum, current) => 1 + '2', 0); results
То есть мы явно складываем число и строку и ТС это не парит.

Для тебя выход: или использовать только числа, или если ты допускаешь, что могут приходить строки, но это все равно числа, то парсить их.
interface MyArray<T> { reduce<T>(fn: (arg:T) => T): T; } let arr = [1, 2, 3, 4, '5']; let results = arr.reduce<number>((sum, current) => sum + (typeof current === "string" ? parseInt(current) : current), 0); results

Обрати внимание, что <number> в такой конструкции обязателен, потому что reduce сам по себе тоже дженерик, и в него можно передать какой тип ожидается в результате. Если ты не указываешь явно, то он берет из входящих параметров. Так как у тебя на входе массив чисел или строк, то он допускает, что sum тоже число или строка, и тоже ругается на то, что его нельзя суммировать. Указав <number> мы ему сообщаем, что результат у него четко number, и тут уже reduce будет требовать, чтобы каждая итерация у тебя возвращала четко number и будет считать, что sum тоже всегда number.

Получается мы принудительно получаем результат number и преобразуем строку в число, но по логике вычисления там включается конкатенация и результат этого выражения должен быть 105, насколько вообще правильно в данном случае так делать или это все-таки зависит от задачи?

Может можно все-таки что-то сделать не ломая логику выражения?
В моем случае нет конкатенации, потому что я складываю уже преобразованные в числа строки и у меня на выходе четко строки, то есть результат 15. В твоем же случае это походит на натягивание совы на глобус. Вот зачем тебе складывать числа со строками?
Просто интересно)
Ну вот ТС тебе говорит "Ты занимаешься фигней", и я с ним в целом согласен :)
Переходи к следующему уроку))

Добавить комментарий