Как я уже сказал, я думаю, что конкретный вывод A
из Reducer<A>
, который вы пытаетесь достичь, может произойти автоматически в TypeScript 3.4 и выше, но это еще не было выпущено.Для TypeScript 3.3 и ниже:
Конкретная техника, которую я видел, использовалась в подобных случаях, когда вы хотите сказать
declare function f<T extends Something<T>>(x: T): void; // circularity error or other issue
, - оставить универсальный T
неограниченными поместите ограничение на параметр функции через пересечение:
declare function f<T>(x: T & Something<T>): void; // works now
Я не могу найти каноническую документацию для этого, но я думаю, что это работает так, что когда вы вызываете f(x)
, компиляторпытается вывести T
из x
типа T & Something<T>
.Поскольку x
является T & Something<T>
, это должно быть T
, как работают пересечения.Таким образом, тип параметра x
используется как T
.Затем он проверит пересечение с Something<T>
, и если это не сработает, вы получите ошибку компилятора.
Давайте попробуем это в вашем случае, но прежде чем мы это сделаем, большое предостережение: вы, вероятно, не можете заставить компилятор вывести параметр типа paginate()
R
из значения, которое вы передаетекак reduce
, и выводят типы параметров значения, которое вы передаете как reduce
из типа R
, выведенного в вызове к paginate()
.То есть либо R
будет выведено как Reducer<number>
, но вам нужно будет аннотировать (acc:number, item:Item, index:number)
... или вам придется укажите R
как Reducer<number>
, и компилятор выведет типы acc, item, index
.Вы хотите это обоими способами, но я не думаю, что компилятор достаточно умен.Или, по крайней мере, я не могу этого сделать.Итак, сейчас я предполагаю, что вы пытаетесь вывести R
из полностью аннотированного обратного вызова reduce
:
interface Item {
t: "item";
}
type Reducer<A> = (acc: A, item: Item, index: number) => A;
type Accumulator<Reduce> = Reduce extends Reducer<infer A> ? A : never;
declare function paginateC<R extends Reducer<any>>(
reduce: R & Reducer<ReturnType<R>> // intersection here
): Promise<Accumulator<R>>;
// Promise<number> as desired
const resultC = paginateC((acc: number, item: Item, index: number) => 5);
// error, string is not number:
paginateC((x: number, y: Item, z: number)=>"string");
// okay, since string is a subtype of unknown
paginateC((x: unknown, y: Item, z: number)=>"string")
// also works with any
paginateC((x: any, y: Item, z: number)=>"string")
Так что все в порядке.
Вернемся к проблеме с выводом acc, item, index
... Вы всегда можете указать R
следующим образом:
// acc, item, index inferred from specified R
paginateC<Reducer<number>>((acc, item, index) => 5);
, но вы не хотите этого делать,
По правде говоря, я не ожидал бы, что компилятор сузит параметр acc
до желаемого значения A
, поскольку параметры функции всегда можно безопасно расширять ( контрастность параметров функции ),Компилятор, вероятно, оставит acc
как что-то широкое, например {}
, unknown
или any
, если вы не аннотируете его или не задаете R
вручную, как указано выше.
I am немного удивлен, что он не сужает item
и index
до Item
и number
соответственно вместо того, чтобы оставить их как any
.Но, вероятно, сделать не так уж и много, так как это известное ограничение вывода типа, что оно не происходит за несколько проходов.Ну хорошо.
Хорошо, надеюсь, это поможет.Удачи!