TypeScript - проверить, является ли свойство объекта функцией с заданной подписью

У меня есть функция, которая получает свойство от объекта.

// Utils.ts
export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
    if (key in obj) { return obj[key as K]; }
    throw new Error(`Invalid object member "${key}"`);
}

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

getProperty() используется для динамического получения одного из методов объекта и вызова его. Я старался:

let property: this[keyof this] = utils.getProperty(this, name);
if (typeof property === 'function') ) { property(conf); }

Но это дает следующее: "Невозможно вызвать выражение, тип которого не имеет подписи вызова. Тип" any "не имеет совместимых подписей вызова". ошибка. Я понимаю, что собственность, которая исходит от getProperty() действительно может быть любого типа, но как убедиться, что это функция с (conf: {}): void подпись?

2 ответа

Решение

Это не похоже typeof защита типов существует для типов функций. Похоже, что это дизайн (этот вопрос о instanceof типа охранников, но я предполагаю, что это аналогичные рассуждения).

Однако в TypeScript есть определенные пользователем средства защиты типов, а это означает, что вы можете написать функцию, которая сужает тип ее аргумента любым удобным вам способом. Итак, какой тест во время выполнения определит, если какое-то значение x является функцией типа (conf: {}) => void? Вы можете проверить, если typeof x === "function", конечно. Это не означает, что функция будет принимать параметр какого-либо определенного типа и ничего не будет возвращать. Может быть, вы просто знаете, что любую функцию вы вытаскиваете из getProperty будет правильный тип. Если нет, вы должны выяснить, как отличить разницу во время выполнения. (Является x.length === 1? Есть ли другая отличительная черта?)

Как только вы узнаете свой тест во время выполнения, вы можете сделать защиту типа:

function isFunctionOfRightType(x: any): x is (conf: {})=>void {
   return (typeof x === 'function') && (x.length === 1); // or whatever test
}

// ... later

let property: this[keyof this] = utils.getProperty(this, name);
if (isFunctionOfRightType(property)) { property(conf); } // no error now

Надеюсь, это поможет. Удачи!

В вашей функции подпись есть ошибка, key параметр должен иметь тип K и это даст вам лучший вывод, когда вы используете константы для параметров:

export function getProperty<T, K extends keyof T>(obj: T, key: string): T[K] {
    if (key in obj) { return obj[key as K]; }
    throw new Error(`Invalid object member "${key}"`);
}

let foo = {
    fn (conf: {}): void {}
}
let fn = getProperty(foo, "fn");
fn({}); // callable 

Проблема, когда вы используете ключ, который является строкой и не проверен во время компиляции, состоит в том, что компилятор не может вам реально помочь. Это предполагает, что, поскольку вы индексируете мою произвольную строку, тип возвращаемого значения может быть любым допустимым типом поля цели. Нет способа проверить типы параметров функции во время выполнения, так как они будут удалены, но вы можете проверить количество параметров:

let property:  Function = getProperty(this, name) as any;
if (typeof property === 'function' && property.length == 1)  
{ 
    property({}); 
}
Другие вопросы по тегам