Я создал универсальный тип функции с возвращаемым параметром, который полностью зависит от типов входных параметров, а универсальный параметр имеет предложение extends. Вот фиктивная версия:
type Foo = {}
type MyFunc <T extends Foo> = (a: T, b: T) => T[];
const bar: MyFunc<number> = (a: number, b: number) => [a,b];
const baz: MyFunc<boolean> = (a: boolean, b: boolean) => [a,b];
Я использую общий параметр, чтобы убедиться, что 1) a и b расширяют Foo и 2) a и b имеют один и тот же тип.
Проблема, с которой я сталкиваюсь, заключается в использовании MyFunc в качестве типа параметра. Мне не нужен точный тип для T; Мне нужно только убедиться, что ограничения параметров выполняются при его вызове. Например, я хотел бы сделать следующее:
const makeArray = (qux: MyFunc) => qux(1,2);
1 и 2 имеют один и тот же тип и оба расширяют Foo, поэтому я знаю, что вызов функции будет работать, и я верну number[]. Однако приведенное выше не компилируется. Типскрипт говорит:
Generic type 'MyFunc' requires 1 type argument(s)
Если я использую (qux: MyFunc<any>), то проверки типов вообще не происходит.
Есть ли какой-либо другой синтаксис, который я могу использовать, чтобы указать TypeScript проверять входные параметры, но не требовать указания точного типа заранее?
В месте вызова он может просто проверить, что a и b имеют один и тот же тип и что они оба реализуют Foo. Этого было бы достаточно, чтобы сделать его типобезопасным, верно? Или вы видите ситуацию, когда это было бы неразумно?
Так вы действительно просто запрашиваете общие параметры по умолчанию? type MyFunc<T extends Foo = Foo>
Вероятно, вы тоже захотите сделать makeArray универсальным и передать этот универсальный тип MyFunc как const makeArray = <T extends Foo>(qux: MyFunc<T>) => qux(1,2).
Это выглядит как путаница с областью действия параметра универсального типа. Возможно, вы ищете что-то вроде this, где тип qux на самом деле является общим. Если это не то, что вы ищете, не могли бы вы точно объяснить, что вы имеете в виду?
@jcalz Да, это именно то, что мне было нужно, спасибо! Просто пришлось переместить часть <...> справа от знака равенства, чтобы T указывалось во время вызова, а не во время определения. Если вы напишите это как ответ, я приму это.






Похоже, вы неправильно определили область действия параметра универсального типа. В TypeScript есть два разных (но связанных) «разновидности» дженериков. См. TypeScript, как создать псевдоним универсального типа для универсальной функции? и разница машинописи между размещением аргументов дженериков например.
Существуют универсальные типы, в которых параметр типа объявляется в самом имени типа, например
// type GenType<T> = ...T...
И есть общие функции, где параметр типа объявлен в сигнатуре вызова, например
// type GenFunc = <T>(...T...)=>...
Изначально у вас был MyFunc как универсальный тип, который оценивается как неуниверсальная функция.
// original
type MyFunc<T extends Foo> = (a: T, b: T) => T[];
но это не похоже на то, что вы хотите. Вместо этого вы должны сделать его неуниверсальным типом, который вычисляет общую функцию:
// changed
type MyFunc = <T extends Foo>(a: T, b: T) => T[];
Итак, теперь вы можете создать функцию типа MyFunc, и она будет действовать как ваша старая MyFunc<T> для всех возможных T:
// actually generic
const foo: MyFunc = (a, b) => [a, b];
Если вы действительно хотите ограничить функцию так, чтобы она принимала аргумент определенного типа, вы можете использовать выражения инстанцирования:
const bar = foo<number>;
// const bar: (a: number, b: number) => number[]
const baz = foo<boolean>;
// const baz: (a: boolean, b: boolean) => boolean[]
Но я не знаю, насколько это полезно.
В любом случае, теперь вы можете реализовать makeArray так, как хотите, где MyFunc не требует аргумента типа:
const makeArray = (qux: MyFunc) => qux(1, 2);
// const makeArray: (qux: MyFunc) => number[]
И смотрите, компилятор делает вывод, что возвращаемый тип makeArray — это number[], как и хотелось!
Как TypeScript должен знать, как проверять входные параметры, если он не знает точного типа?