The question accepted by the owner (until now) is incorrect.
Here's how you can do it:
You need to make the index signature a union type of all the types that can be contained in the interface:
interface IExample {
hello?: string;
moo?: boolean;
[custom: string]: string | boolean | YourFunctionType;
}
interface YourFunctionType {
(v?: any): boolean;
}
Please note that I've extracted your function type into a separate interface to improve readability.
Implications:
This means, that the explicitly defined properties are well supported by TS:
const test: IExample = <IExample>{};
test.hello.slice(2); // using a string method on a string --> OK
const isHello = test.hello === true; // ERROR (as expected): === cannot be applied to types string and boolean
const isMoo2 = test.moo === true; // OK
However all properties from the index signature now need to be checked using type guards which adds a little bit of a runtime overhead:
test.callSomething(); // ERROR: type 'string | boolean | YourFunctionType' has no compatible call signatures
if (typeof test.callSomething === 'function') { // alternatively you can use a user defined type guard, like Lodash's _.isFunction() which looks a little bit nicer
test.callSomething(); // OK
}
On the other hand: the runtime overhead is necessary because it might be that test
is accessed like this:
const propertyName: string = 'moo';
test[propertyName](); // ERROR: resolves to a boolean at runtime, not a function ...
// ... so to be sure that an arbitrary propertyName can really be called we need to check:
const propertyName2: string = 'arbitraryPropertyName';
const maybeFunction = test[propertyName2];
if (typeof maybeFunction === 'function') {
maybeFunction(); // OK
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…