Требуется что-то вроде частичного вывода параметров типа, который в настоящее время не является функцией TypeScript (см. microsoft / TypeScript # 26242 ). Прямо сейчас вы должны указать все параметры типа вручную или позволить компилятору определить все параметры типа; нет частичного вывода. Как вы заметили, generi c параметр типа по умолчанию do not scratch this зуд; по умолчанию отключается логический вывод.
Таким образом, существуют обходные пути. Те, которые работают последовательно, но также несколько раздражают в использовании, это либо curry , либо "dummying". Выделение карри означает, что вы разделяете одну функцию с несколькими типами аргументов на несколько функций с одним типом аргумента:
type Obj = Record<string, any>; // save keystrokes later
declare const createTaskCurry:
<I extends Obj = {}>() => <O extends Obj>(t: TaskFunction<I, O>) => Task<I, O>;
createTaskCurry()(a => ({ foo: "" }));
// I is {}, O is {foo: string}
createTaskCurry<{ bar: number }>()(a => ({ foo: "" }));
// I is {bar: number}, O is {foo: string}
createTaskCurry<{ bar: number }>()<{ foo: string, baz?: number }>(a => ({ foo: "" }));
// I is {bar: number}, O is {foo: string, baz?: number}
У вас есть точное поведение, которое вы хотите по отношению к типам I
и O
, но в этом есть раздражающий отложенный вызов функции.
Подстановка здесь означает, что вы задаете для функции фиктивный параметр типов, которые вы хотите указать вручную, и позволяете выводу заменять ручная спецификация:
declare const createTaskDummy:
<O extends Obj, I extends Obj = {}>(t: TaskFunction<I, O & {}>,
i?: I, o?: O) => Task<I, O>;
createTaskDummy(a => ({ foo: "" }));
// I is {}, O is {foo: string}
createTaskDummy(a => ({ foo: "" }), null! as { bar: number });
// I is {bar: number}, O is {foo: string}
createTaskDummy(a => ({ foo: "" }), null! as { bar: number },
null! as { foo: string, baz?: number });
// I is {bar: number}, O is {foo: string, baz?: number}
Снова, у вас есть поведение, которое вы хотите, но вы передаете бессмысленные / фиктивные значения функции.
Конечно, если у вас уже есть параметры правильные типы, вам не нужно добавлять «фиктивный» параметр. В вашем случае вы, безусловно, можете предоставить в параметре task
достаточно информации, чтобы компилятор мог вывести I
и O
, пометив или указав другие типы в параметре task
:
declare const createTaskAnnotate:
<O extends Obj, I extends Obj = {}>(t: TaskFunction<I, O>) => Task<I, O>;
createTaskAnnotate(a => ({ foo: "" }));
// I is {}, O is {foo: string}
createTaskAnnotate((a: { bar: number }) => ({ foo: "" }));
// I is {bar: number}, O is {foo: string}
createTaskAnnotate((a: { bar: number }): { foo: string, baz?: number } => ({ foo: "" }));
// I is {bar: number}, O is {foo: string, baz?: number}
Это, вероятно, решение, которое я бы порекомендовал здесь, и оно фактически совпадает с другим ответом, опубликованным . Таким образом, весь этот ответ кропотливо объясняет, почему то, что вы хотите сделать в настоящее время, невозможно и почему доступные обходные пути уводят вас от этого. Ну хорошо!
Хорошо, надеюсь, это поможет разобраться в ситуации. Удачи!
Детская площадка ссылка на код