Необязательность аргумента дочерней функции одной функции зависит от необязательности аргумента другой функции.

Я создаю пример функции, которая принимает функцию fn1 в качестве аргумента и возвращает функцию fn2;

type Values<T> = (values: T) => Promise<void>;  //function with argument
type NoValues = () => Promise<void>;    //function without arguments
type Func1<T> = Values<T> | NoValues;

let example = function <T, R extends Func1<T> = Func1<T>>(fn1: R) {  
    const fn2 = async (values: R extends Values<T> ? T : void) => { 
        await fn1(values);
    }
    return fn2;
}

fn1 должен принимать один необязательный аргумент. В зависимости от того, имеет ли fn1 необязательный аргумент, он также должен быть у fn2. Таким образом, если у fn1 НЕТ аргументов, то у fn2 их тоже не должно быть, и наоборот.

Я пробовал писать такие ограничения разными способами, но все никак не получается. Буду рад, если мне кто-нибудь поможет.

interface IExample {
    whatever: string;
}
const test1_fn1 = async () => console.log("");
const test1_fn2 = example<IExample>(test1_fn1);
test1_fn2() //Should work, but does not. Expected 1 argument. Not intended

const test2_fn1 = async (values: IExample) => console.log(values);
const test2_fn2 = example<IExample>(test2_fn1);
test2_fn2() //Should not work, does not work, as intended
test2_fn2({whatever: ""}) //Should work, works, as intended

Версия TS: 3.8.3. Вот площадка


person Timur Rakhimzhan    schedule 17.11.2020    source источник


Ответы (1)


Наверное, проще всего с перегрузкой:

type Values<T> = (values: T) => Promise<void>;  //function with argument
type NoValues = () => Promise<void>;    //function without arguments
type Func1<T> = Values<T> | NoValues;

function makeFn2<T>(fn1: NoValues): NoValues;
function makeFn2<T>(fn1: Values<T>): Values<T>;
function makeFn2<T>(fn1: Func1<T>): Func1<T> {  
    return async (...values: [T?]) => { 
        await (fn1 as any)(...values);
    }
}


declare const argFunc: Values<string>;
const argFunc2=makeFn2(argFunc);
argFunc2("s");
argFunc2();  // error

declare const noArgFunc: NoValues;
const noArgFunc2=makeFn2(noArgFunc);
noArgFunc2();
noArgFunc2("asd");  // error

Он должен быть правильным как при компиляции, так и во время выполнения, т.е. fn1 вызывается с аргументом, только если с ним вызывалась fn2.

person TPReal    schedule 17.11.2020
comment
Спасибо за ответ. Это то, что мне было нужно. Разве вы не знаете, как добиться того же поведения с использованием условных типов и без использования перегрузки. Я просто потратил много времени, пытаясь решить эту проблему таким образом, и был бы счастлив посмотреть, что я сделал не так. - person Timur Rakhimzhan; 17.11.2020
comment
@TimurRakhimzhan Добавил вариант без перегрузок, но думаю перегруз лучше. - person TPReal; 17.11.2020
comment
Спасибо за помощь! - person Timur Rakhimzhan; 17.11.2020
comment
Решение без перегрузки к сожалению не работает. Я пробовал это на детской площадке. Вы можете обновить свой ответ? @TPReal - person Timur Rakhimzhan; 17.11.2020
comment
@TimurRakhimzhan Извините, моя ошибка. Похоже, я не могу сделать это каким-либо разумным способом. Можно объявить function makeFn2<T, F extends Func1<T>>(fn1: F): F extends NoValues ? NoValues : Values<T>, но тогда вам нужно вызвать makeFn2<string,Values<string>>(argFunc), так что оно того не стоит. Перегрузка - хорошее решение. - person TPReal; 17.11.2020