Trying to make a static method that accept only instance method prop names, unfortunately out of ideas how to make this work with the following case. Any thoughts (please no type assertion in the call signature)?
Playground
type MethodKeys<T extends object> = keyof { [K in keyof T as T[K] extends (...args: any) => any ? K : never]: unknown };
class Model{
static chainMethod<C extends typeof Model, T extends InstanceType<C>, K extends MethodKeys<T>>
(this: C, name: K, method: T[K]) {
}
method(){
}
}
class Party{
static addProps<C extends typeof Model>(model: C){
model.chainMethod('method', function(){ // error
})
}
}
Another try:
Playground
class Model{
static chainMethod<C extends typeof Model, T extends InstanceType<C>, K extends keyof T, F extends T[K] extends (...args: any) => any ? T[K]: never>
(this: C, name: K, method: F) {
}
method(){
}
}
class Party{
static addProps<C extends typeof Model>(model: C){
model.chainMethod('method', function(){
})
}
}
UPDATE
Matt Kantor has solved the second solution, but I would still prefer the first one where autocomplete would show only method names
Trying to make a static method that accept only instance method prop names, unfortunately out of ideas how to make this work with the following case. Any thoughts (please no type assertion in the call signature)?
Playground
type MethodKeys<T extends object> = keyof { [K in keyof T as T[K] extends (...args: any) => any ? K : never]: unknown };
class Model{
static chainMethod<C extends typeof Model, T extends InstanceType<C>, K extends MethodKeys<T>>
(this: C, name: K, method: T[K]) {
}
method(){
}
}
class Party{
static addProps<C extends typeof Model>(model: C){
model.chainMethod('method', function(){ // error
})
}
}
Another try:
Playground
class Model{
static chainMethod<C extends typeof Model, T extends InstanceType<C>, K extends keyof T, F extends T[K] extends (...args: any) => any ? T[K]: never>
(this: C, name: K, method: F) {
}
method(){
}
}
class Party{
static addProps<C extends typeof Model>(model: C){
model.chainMethod('method', function(){
})
}
}
UPDATE
Matt Kantor has solved the second solution, but I would still prefer the first one where autocomplete would show only method names
1 Answer
Reset to default 4I believe this has the behavior you want:
Playground
class Model{
static chainMethod<C extends typeof Model, K extends keyof InstanceType<C>, F extends InstanceType<C>[K]>
(this: C, name: K, method: F & ((...args: never) => unknown)) {
}
nonMethod?: string
method(){
}
}
class Party{
static addProps<C extends typeof Model>(model: C){
model.chainMethod('method', function(){})
// these should all have errors:
model.chainMethod('method', function(x: string){})
model.chainMethod('nonMethod', 'test')
model.chainMethod('nonExistentKey', function(){})
}
}
It's a good rule of thumb to use as few type parameters as possible in generic function types. I also replaced your conditional type (to check method-ness) with an intersection, as that's simpler for the type checker to reason about.
As per your update, here's another version with different autocomplete behavior:
Playground
type MethodKeys<T> = keyof {
[K in keyof T as T[K] extends (...args: never) => unknown ? K : never]: unknown
}
class Model{
static chainMethod<T extends Model, K extends MethodKeys<T>, F extends T[K]>
(this: abstract new (...args: never) => T, name: K, method: F) {
}
}