I'm trying to write a function that takes a model class and calls .findAll({}), but TypeScript is giving me an error. This reproduces it:
async function createResource(model: typeof Model) {
const results = await model.findAll({})
}
gives:
The 'this' context of type 'typeof Model' is not assignable to method's 'this' of type 'new () => Model<any>'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
Seems this might be a deeper issue in TypeScript: https://github.com/Microsoft/TypeScript/issues/5843#issuecomment-348356421
You can achieve this like so:
type NonAbstract<T> = {[P in keyof T]: T[P]}; // "abstract" gets lost here
type Constructor<T> = (new () => T);
type NonAbstractTypeOfModel<T> = Constructor<T> & NonAbstract<typeof Model>;
function createResource<T extends Model<T>>(model: NonAbstractTypeOfModel<T>): Promise<T[]> {
return model.findAll<T>();
}
Now you can use it without passing any generic type explicitly. The proper type is inferred automatically by typescript:
createResource(User)
.then(users => users[0]. ...) // <-- autocompletion should work very well here
;
How obvious, thanks! This solves my issue, but FYI, yours gives:
Type 'Bluebird<T[]>' is not assignable to type 'Promise<T[]>'.
Property '[Symbol.toStringTag]' is missing in type 'Bluebird<T[]>'.
So you need to return model.findAll<T>() as any as Promise<T[]>.
You need to use bluebird as the promise type:
import * as Promise from 'bluebird' Then the error disappears as well :)
Most helpful comment
You can achieve this like so:
Now you can use it without passing any generic type explicitly. The proper type is inferred automatically by typescript: