簡體   English   中英

TypeScript Generics 帶 TypeSafety 的多種返回類型

[英]TypeScript Generics Multiple Return Types With TypeSafety

我正在開發一個多租戶應用程序,其中,我結合使用 mongoose 和 typegoose 來為特定租戶切換數據庫。

下面是通過安裝 typegoose 和 mongoose 包可以看到的最小可重現代碼。

 import { getModelForClass, mongoose, prop } from '@typegoose/typegoose'; import { AnyParamConstructor, ReturnModelType } from '@typegoose/typegoose/lib/types'; export class DB_ROLES { @prop({ type: String }) name: string; } const ROLES_MODEL = getModelForClass(DB_ROLES, { schemaOptions: { timestamps: true, collection: 'roles', }, }); export class DB_USERS { @prop({ type: String }) name: string; } const DB_USERS_MODEL = getModelForClass(DB_USERS, { schemaOptions: { collection: 'users', timestamps: true, }, }); //DATABASE UTILITY class DatabaseUtil { public database: mongoose.Connection; public connectDatabase = async (): Promise<boolean> => { return new Promise((resolve, reject) => { const uri = environment_variables.MONGODB_CONNECTION_STRING?? ''; if (this.database) { return; } mongoose.connect(uri, {}); this.database = mongoose.connection; this.database.once('open', async () => { console.log('Connected to database'); resolve(true); }); this.database.on('error', () => { console.log('Error connecting to database'); reject(false); }); }); }; getModelForDb<T extends AnyParamConstructor<any>>(databaseName: string, model: ReturnModelType<T>): ReturnModelType<T> & T { const db = Mongoose.connection.useDb(databaseName); const DbModel = db.model(model.modelName, model.schema) as ReturnModelType<T> & T; return DbModel; } getModelsForDbWithKey<P extends string, T extends AnyParamConstructor<any>, K extends { key: P; model: ReturnModelType<T> }>( databaseName: string, models: K[] ): Partial<Record<P, ReturnModelType<T> & T>> { const db = mongoose.connection.useDb(databaseName); let result: Partial<Record<P, ReturnModelType<T> & T>> = {}; let allModels: (ReturnModelType<T> & T)[] = []; models.forEach((value) => { result[value.key] = db.model(value.model.modelName, value.model.schema) as ReturnModelType<T> & T; }); return result; } } const DBUtil = new DatabaseUtil(); let singleModel = DBUtil.getModelForDb('base_db', ROLES_MODEL); singleModel.find({ //INTELLISENSE WORKS HERE }); let requiredModels = DBUtil.getModelsForDbWithKey('base_db', [ { key: 'roles', model: ROLES_MODEL }, { key: 'users', model: DB_USERS_MODEL }, ]); let roleModel = requiredModels['roles']?.find({ //INTELLISENSE DOESN'T WORK });

當我使用單個 Model 時,我也會得到智能感知

在此處輸入圖像描述

現在我無法輸入我已通過的 model 的類型。

在此處輸入圖像描述

這就是我在 hover 超過所需型號 object 時得到的回報。 那么有什么方法可以讓我用 Generics 獲得正確的類型。 模型可以具有不同的模式,因此具有不同的返回類型。

對於您的情況,您可能正在尋找一種類型,該類型可以將 map 從數組轉換為 object 而不會丟失類型(就像我在閱讀此問題后在這里問過的那樣)。

結果代碼將是:

// your models before here
// Extracted the type from the constraint to a interface
interface ModelListEntry {
  key: string;
  model: ReturnModelType<any>;
}

// Mapper type that maps a input array of "ModelListEntry" to a Record where the key is from "key" and the value is "model"
// the key-value information is only kepts if the input array is readonly (const)
type ModelMapListEntryArrayToRecord<A extends readonly ModelListEntry[]> = {
  // see https://stackoverflow.com/a/73141433/8944059
  [E in Extract<keyof A, `${number}`> as A[E]['key']]: A[E]['model'];
};

// DATABASE UTILITY
class DatabaseUtil {
  public database!: mongoose.Connection;
  public connectDatabase = async (): Promise<boolean> => {
    return new Promise((resolve, reject) => {
      const uri = 'mongodb://localhost:27017/';

      if (this.database) {
        return;
      }

      mongoose.connect(uri, {});
      this.database = mongoose.connection;
      this.database.once('open', async () => {
        console.log('Connected to database');
        resolve(true);
      });
      this.database.on('error', () => {
        console.log('Error connecting to database');
        reject(false);
      });
    });
  };

  getModelForDb<T extends AnyParamConstructor<any>>(databaseName: string, model: ReturnModelType<T>): ReturnModelType<T> & T {
    const db = mongoose.connection.useDb(databaseName);
    const DbModel = db.model(model.modelName, model.schema) as ReturnModelType<T> & T;

    return DbModel;
  }

  getModelsForDbWithKey<List extends readonly ModelListEntry[]>(databaseName: string, models: List): ModelMapListEntryArrayToRecord<List> {
    const db = mongoose.connection.useDb(databaseName);
    const result: Record<string, ModelListEntry['model']> = {};
    for (const { key, model } of models) {
      result[key] = db.model(model.modelName, model.schema);
    }

    return result as any;
  }
}

const DBUtil = new DatabaseUtil();
const singleModel = DBUtil.getModelForDb('base_db', ROLES_MODEL);
singleModel.find({
  // INTELLISENSE WORKS HERE
});

const requiredModels = DBUtil.getModelsForDbWithKey('base_db', [
  { key: 'roles', model: ROLES_MODEL },
  { key: 'users', model: DB_USERS_MODEL },
] as const); // change the input array to be read-only, otherwise typescript will just "forget" about the explicit values

requiredModels.roles; // correct type "ReturnModelType<typeof DB_ROLES>"
requiredModels.users; // correct type "ReturnModelType<typeof DB_USERS>"
requiredModels.none; // error

const roleModel = requiredModels['roles']?.find({
  // INTELLISENSE WORKS NOW
});

PS:這個答案歸功於上述問題及其評論的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM