#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
это будет именно так, но, видимо, нет! Спасибо.