Отражение метода с параметрами и типом возврата от объекта с использованием условных типов - PullRequest
0 голосов
/ 08 апреля 2020

Я пытаюсь вызвать функцию с отражениями и одновременно сохранить ее типы.

Кажется, что T extends OnlyFunctions<Foo> работает нормально, но K, похоже, не "помнит", что T состоит только из AnyFunctions.

Я получаю ошибку: Type 'T[K]' does not satisfy the constraint '(...args: any) => any'

Как решить эту проблему?


type AnyFunction = (...args: any[]) => any;

type Only<T, S> = {
    [K in keyof T]: T[K] extends S ? K : never
}[keyof T];

type OnlyFunctions<T> = Pick<T, Only<T, AnyFunction>>;


interface Foo {
    a: (x1: number, x2: number) => number;
    b: (y: string) => string;
}

const foo: Foo = {
    a: (x: number, x1: number) => { return x + x1 + 1 },
    b: (y: string) => { return y + "y" },
}
const invoke = <T extends OnlyFunctions<Foo>, K extends keyof T>(
    method: K,
    ...args: Parameters<T[K]>,
): ReturnType<T[K]> => {
    return foo[method].call(foo, args)
};

invoke("a", 1, 2)

1 Ответ

1 голос
/ 08 апреля 2020

Я вижу несколько проблем с вашим примером кода; тот, который имеет непосредственное отношение к вашему вопросу, состоит в том, что T является обобщенным c типом, который расширяет OnlyFunctions<Foo>, что означает, что он вполне может иметь больше свойств, чем заявлено в OnlyFunctions<Foo>, благодаря структурному подтип работает в TypeScript (типы объектов в TypeScript открыты , а не точные ). Если T extends {foo: string}, то T может быть {foo: string, bar: number} или {foo: string, baz: boolean}. Таким образом, компилятор не может предположить, что известные свойства T являются только функциями, даже если известные свойства OnlyFunctions<Foo> являются только функциями.

Я бы предложил полностью удалить T, так как это не делает вас что-нибудь хорошее в этих примерах (из этого ничего не следует сделать), и замените его на OnlyFunctions<Foo>. Вы можете оставить K.

После этого все еще есть несколько проблем ... неверная запятая, использование call вместо apply (или, возможно, использование args вместо ...args, в зависимости от того, как вы на это смотрите), и тот факт, что когда вы вызываете apply или call на Foo[K], компилятор бесполезно расширяет свой тип до объединения перегруженных функций, которые к сожалению не вызывается . Мое решение будет выглядеть следующим образом:

const invoke = <K extends keyof OnlyFunctions<Foo>>(
    method: K,
    ...args: Parameters<Foo[K]>
): ReturnType<Foo[K]> => {
    const fooMethod: { apply(f: Foo, args: Parameters<Foo[K]>): ReturnType<Foo[K]> } =
        foo[method];
    return fooMethod.apply(foo, args)
};

Здесь я привел компилятор к более полезному типу для foo[method], назначив его промежуточной переменной с именем fooMethod, чей метод apply() как известно, подходящего типа. Это компилируется и работает правильно:

console.log(invoke("a", 1, 2)) // 4

Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...