Home > Enterprise >  How to infer class declaration in dynamic context in typescript
How to infer class declaration in dynamic context in typescript

Time:01-16

I'm building a library and having a problem inferring type dynamically.

This is library side class and useModel returns Model instance

class Database {
  ...
  public useModel(target: Function) {
    const tableName = getClassMetadata(target).name;
    const table = this.config.tables.find(({ name }) => name === tableName);

    // Problem is here - Not infering
    type RType = typeof target extends (...args: any[]) => infer RT ? RT : Function;
    // Model has selectAll() which returns RType[]
    return new Model<RType>(this.connection, table);
  }
}

As a library consumer, usage is shown below.

I have custom decorators to add metadata values.

@Table({ name: 'Users', timestamps: true })
class Users {
  @PrimaryKey({autoIncrement: true, unique: true,})
  id: number;

  @Indexed({ unique: true })
  @Default('John')
  username: string;

  @Default(0)
  @Indexed({ unique: false })
  age: number;
}

const database = new Database({
  version: 1,
  name: 'MyDatabase',
  tables: [Users],
});

// I'd like Users class to be infered in useModel
const usersModel = database.useModel(Users);
usersModel.selectAll().then((users) => {
  users?.forEach((user) => {
    // user is not infered here :( 
    console.log(user);
  });
});

If I use generic in useModel I know it will work but I don't want to use generic value as const usersModel = database.useModel<typeof Users>(Users);

My goal is that Users class to be inferred automatically from useModel

This is desired usage const usersModel = database.useModel(Users);

Any suggestions?

CodePudding user response:

You don't need a conditional type for this, a simple generic type parameter is all you need as in the following

class Database {

  useModel<T>(target: new () => T): Model<T> {
    const tableName = getClassMetadata(target).name;
    const table = this.config.tables.find(({ name }) => name === tableName);
    
    return new Model<T>(this.connection, table);
  }
}

The reason that original version did not work is because conditional types must be determined at compile time and cannot depend on a particular path of execution.

  •  Tags:  
  • Related