Вывод типа ‘1’ вместо числа

#typescript

#typescript

Вопрос:

У меня есть этот код typescript

 type X = (<T extends number | string>(a: T) => (b: T) => T)

const f: X = (a: any) => (b: any) => a   b

f(1)(2)
  

Он показывает ошибку

 Argument of type '2' is not assignable to parameter of type '1'.
  

Выводимый тип в первом аргументе — '1' но я хочу, чтобы он был number с теми же ограничениями для дженериков string | number .

Типы передаваемых аргументов могут быть только либо number или string , т.е. Ограничены string | number .

Игровая площадка

Ответ №1:

Будет ли это работать в вашем случае?

 type XGen<T extends string | number> = (a: T) => (b: T) => T;
type X = XGen<string> amp; XGen<number>;
const f: X = (a: any) => (b: any) => a   b;

f(1)(2);
f(1)('2'); // error
f('1')('2');
f([])([]); // error
  

Цель состоит в том, чтобы указать TypeScript выбирать только между string и number (не угадывая никаких других типов, которые расширяют их объединение (например 1 , как в вашем примере)). Мы добились этого, создав базовый универсальный вспомогательный тип XGen и создав тип пересечения XGen<string> и XGen<number> (поэтому заставляя typescript выбирать только между этими двумя).

Комментарии:

1. Нет, я хочу f быть универсальной функцией. В моем случае f("ab")("cd") также должно работать.

2. @ShivamSingla В этом случае определение другой функции, такой как fStr: X<string> мне кажется, намного понятнее. Однако я понимаю, что это может не вписываться в вашу общую картину здесь.

3. Объявление другой функции нарушит принцип DRY. В таком коде это становится очень важным.

4. @ShivamSingla Я обновил свое решение. К сожалению, это позволит f(1)('2') . Я не уверен, сможете ли вы с этим смириться.

5. @ShivamSingla Будем надеяться, что последнее редактирование — это то, что вы ищете! 🙂

Ответ №2:

почему <T extends number | string> , а не просто <T=number|string> ?

Я только что попробовал

 type X = (<T = number | string>(a: T) => (b: T) => T)

const f: X = (a: any) => (b: any) => a   b

console.log( f(1)(2) );
console.log( f("1")("2") );
  

Результат:

 [LOG]: 3 
[LOG]: "12" 
  

на той же игровой площадке, которую вы нам дали, и она работала просто отлично.

Прошло много времени с тех пор, как я в последний раз писал какой-либо код TS, но я думаю, что когда вы используете extends constraint , вы подразумеваете, что T будет некоторым производным типом, а единственным доступным типом, соответствующим f (1), был какой-то «буквальный тип». Я имею в виду, 1 был «обновлен» с number литерального типа «1» (те же механизмы, что и с литеральными строками как типами и т. Д., Только с числовыми литералами вместо строковых литералов)..

Бьюсь об заклад, вы на самом деле не имели в виду, что тип X является потомком Number или потомком String .. мне кажется, что это несколько … редко..

Комментарии:

1. Таким образом, T не будет ограничено number | string . Он получит тип по умолчанию как number | string . f([])([]) также будет работать, что не требуется.

2. @ShivamSingla Не могли бы вы уточнить свой вопрос с этими деталями?

3. @gurisko обновлено!

4. @ShivamSingla f([])([]) интересно. Я не ожидал, что это будет возможно. Мне придется несколько раз переосмыслить это, потому что, как я уже сказал, прошло много времени с тех пор, как я работал в TS 🙂 Ответ Гуриско звучит для меня очень похоже на то, чего я пытался достичь, но я на самом деле забыл о возможности обозначения amp; типов пересечения. Очень удобная функция. У меня сложилось впечатление, что T = number | string это будет именно так, но, видимо, нет! Спасибо.