TypeScript过滤函数属性时调用报错:无法调用缺少调用签名的表达式
Ah, I get it, let's tackle this TypeScript issue you're facing! Even though your type definition correctly filters down to only function property keys, TypeScript still throws that "Cannot invoke an expression whose type lacks a call signature" error when you try to run t[func](). Here's why that happens and how to fix it:
Why the Error Occurs
Your K type correctly narrows down to keys that map to functions, but when K is a union type (e.g., if your object has multiple function properties), t[func] becomes a union of those function types. TypeScript doesn't allow direct invocation of union function types by default—even though every member is a function, the compiler can't guarantee they all have compatible call signatures, so it plays it safe and throws the error.
Fixes You Can Use
1. Simple Type Assertion (Quick & Clean)
The easiest way to resolve this is to explicitly assert that t[func] is a function. Since you've already constrained K to function-only keys, this assertion is safe:
// First, define a helper type to get function-only keys type FunctionKeys<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; function call<T>(t: T, func: FunctionKeys<T>) { // Assert the value is a function to satisfy the compiler (t[func] as Function)(); }
2. Type Guard (More Strict)
If you prefer to avoid type assertions entirely, you can add a runtime type guard to confirm the value is a function. This adds a tiny bit of redundancy but makes the type checking more explicit:
type FunctionKeys<T> = { [K in keyof T]: T[K] extends Function ? K : never }[keyof T]; function call<T>(t: T, func: FunctionKeys<T>) { const fn = t[func]; // Runtime check that tells TypeScript this is definitely a function if (typeof fn === 'function') { fn(); } }
3. Refined Type Parameters (Precise)
You can also restructure the type parameters to directly enforce that T[K] is a function, leaving no room for ambiguity:
function call<T, K extends keyof T>(t: T, func: K): T[K] extends (...args: any[]) => any ? void : never { return (t[func] as (...args: any[]) => any)(); }
Test It Out
All three fixes will work with an example object like this:
const myObj = { username: "jsdev", sayHi: () => console.log("Hello!"), getCount: () => 100 }; call(myObj, "sayHi"); // ✅ Works call(myObj, "getCount"); // ✅ Works call(myObj, "username"); // ❌ Compile error (as expected)
Pick the approach that fits your code style best—all of them will resolve the call signature error while keeping your original key filtering behavior intact.
内容的提问来源于stack exchange,提问作者Sergey Sokolov

