Is is possible in typescript to define an interface that will only be applied to methods that starts with a certain word and cause them to have a certain return type? I thought of something like:
interface IPattern {
[k: 'handle' string]?: () => SomeClass
}
So that a class that implements IPatten and have methods that start with handle would be force to return a SomeClass object, while other methods would not, example:
class ForcedClass implements IPattern{
foo(): number; // ok
handleFoo(): SomeClass; // ok
handleBar(): number; // error
}
I know a could use an abstract class with all the method that I need, but since the combination of handle something
is used in a lot of different implementations, it would be verbose to create an abstract to each of them;
CodePudding user response:
You can get pretty close with an index signature and template literal types:
interface IPattern {
[k: `handle${string}`]: () => SomeClass
}
class ForcedClass implements IPattern{
[k: `handle${string}`]: () => SomeClass
foo(): number { return 0 } // ok
handleFoo(): SomeClass { return "0 "} ; // ok
handleBar(): number{ return 0 } // error
}
new ForcedClass()['handleBaz'](); // this is ok, because of the index signature
new ForcedClass().handleBaz(); // this is ok, because of the index signature
The problem with the index approach, is that while the class members are validated against it, it also means you can index with any value that conforms to the handle${string}
template literal type, even if the member is not actually part of the class.
The other option would be use a type to validate actual keys of the class without adding the index signature:
type IPattern<T> = Record<Extract<keyof T, `handle${string}`>, () => SomeClass> {
class ForcedClass implements IPattern<ForcedClass>{
foo(): number { return 0 } // ok
handleFoo(): SomeClass { return "0 "} ; // ok
handleBar(): number{ return 0 } // error
}
new ForcedClass().handleBaz(); //error
new ForcedClass().handleFoo(); //ok