I have the following function:
type DefaultEntity = {
id: string;
createdBy: string;
[fieldName: string]: unknown;
};
abstract find<T, Schema extends string | (new () => T)>(
schema: Schema,
id: string,
): Promise<(Schema extends string ? DefaultEntity : T) | null>;
When I call it with a string as schema
the return type is DefaultEntity
. However if I use MyClass
for the schema argument the return type is unknown
not MyClass
. It should be MyClass
because T
becomes MyClass
. Did I miss something or is this something TypeScript just cannot do?
To infer the type of Schema you don't need an additional T
type parameter. You just have to infer the instance-type of the class either by using the infer T
keyword or using the built in helper InstanceType<Class>
. Like this:
type DefaultEntity = {
id: string;
createdBy: string;
[fieldName: string]: unknown;
};
abstract class Base {
// The first way
abstract find<Schema extends string | (new () => any)>(
schema: Schema,
id: string,
): Promise<(Schema extends (new () => infer T) ? T : DefaultEntity) | null>;
// this also works
abstract find2<Schema extends string | (new () => any)>(
schema: Schema,
id: string,
): Promise<(Schema extends (new () => any) ? InstanceType<Schema> : DefaultEntity) | null>;
}
class UserEntity {
id: string = ''
createdBy: string = ''
name: string = ''
}
class BaseImpl extends Base {
async a () {
(await this.find('asdfqer' , 'asdfasd')) satisfies DefaultEntity | null;
(await this.find(UserEntity, 'asdf') ) satisfies UserEntity | null;
}
}
Note: it's better to use overloads or even separate methods, i.e findByName(string, string)
and findByEntity(Schema, string)
, it would be much easier to maintain and read in the future.