Reputation: 7005
I am building some helper classes that operate on or get some specific DOM element types.
So I have for example an HTMLDivElementHelper
all public methods should return HTMLDivElement
.
Those methods contain at least one params but they can also contain 2.
I am trying to force this with an interface or similar but I can not find how to do it.
If I use class MyHelper implements Record<string, () => HTMLDivElement>
Typescript complains that Property 'setValue' of type '(selector: string) => HTMLDivElement' is not assignable to 'string' index type '() => HTMLDivElement'.(2411)
Is this possible?
Upvotes: 2
Views: 877
Reputation: 329388
If you'd like to allow your class to have members that aren't functions, then you can't use a string index signature, nor can you say implements Record<string, XXX>
because the Record<K, V>
utility type is the same as an index signature when your key type is string
.
Instead, you can come up with a generic type CheckMethods<T>
that acts as a constraint on your class. The idea is that you write class MyHelper implements CheckMethods<MyHelper> { /* ... */ }
, where CheckMethods<MyHelper>
will evaluate to something compatible with MyHelper
if and only if all the methods (well, function-valued members) are of the type you want them to be. This sort of self-referential constraint is known as "F-bounded quantification". Here's a possible implementation:
type CheckMethods<T> = { [K in keyof T]: T[K] extends Function ?
((selector: string, options: any) => HTMLDivElement) : T[K] }
Here CheckMethods<T>
is a mapped type where each property key K
from T
is mapped to a new property type. The old property type T[K]
is checked with a conditional type to see if it's a Function
or not. If it's not, we just map it to itself, T[K]
, which will always be true. If it is a function, then we map it to a specific function type (selector: string, options: any) => HTMLDivElement
, which will work with any function of up to two parameters of the same type (see the documentation for function compatiblity for more information.
Let's see it in action:
class MyHelper implements CheckMethods<MyHelper> {
nonMethod = 123; // okay
setValue(selector: string): HTMLDivElement { // okay
return document.createElement('div');
}
setTest(selector: string, options: { options: string }): HTMLDivElement { // okay
return document.createElement('div');
}
badParameter(selector: number): HTMLDivElement { // error!
return document.createElement("div");
}
badReturn(selector: string) { return 3 } // error!
}
Looks good!
Upvotes: 5
Reputation: 6240
Try this:
It compiles but I'm not sure that this is what you are looking for.
type MethodSignature = (selector: string, options?:{options: string}) => HTMLDivElement;
class MyHelper implements Record<string, MethodSignature> {
[k: string]: MethodSignature;
setValue(selector: string): HTMLDivElement {
return document.createElement('div');
}
setTest(selector: string, options?: {options: string}): HTMLDivElement {
return document.createElement('div');
}
}
const myHelper = new MyHelper();
Upvotes: 0