如何获得对 MongoDB 集合的通用和静态引用?

How do I get a Generic and static reference to a MongoDB collection?

我想编写一个静态 getOne<T>() class 方法(即它不需要 class 实例或 new 运算符来使用它)这也是泛型,which returns MongoDB 对象作为 Item,即 BookFilm。这是我最初的想法,但现在我不知道如何动态地使用 'books''films' 集合名称字符串来代替 'xxx'.

import * as mongodb from 'mongodb';

const CONNECTION_STRING = 'MY_CONNECTION_STRING';
const BOOK_COLLECTION_NAME = 'books';
const FILM_COLLECTION_NAME = 'films';

interface Item {
  _id: string
}

interface Book extends Item {
  bookName: string,
  description?: string
}

interface Film extends Item {
  filmTitle: string,
  duration?: number
}

function isBook(item: Item): item is Book {
  return 'bookName' in item;
}

async function getOne<T extends Item>(): Promise<T | null> {

  const client = new mongodb.MongoClient(CONNECTION_STRING);
  await client.connect();
  const db = client.db();

  const collection = db.collection<T>('xxx');
  // Instead of xxx I need to use BOOK_COLLECTION_NAME or FILM_COLLECTION_NAME
  // depending on the actual type of T, how do I do that???
  const item = await collection.findOne({});
  if (item !== null) {
    console.log(item._id);
    if (isBook(item)) {
      // Book type specific implementation
      item.bookName += ' (best book ever)';
    }
  }
  return item as T;
}

async function test() {
  const book: Book | null = await getOne<Book>();
  console.log(book?.bookName);
  const film: Film | null = await getOne<Film>();
  console.log(film?.filmTitle);
}

test();

这可能吗?我意识到我可以解决这个问题,将它作为参数传递给 await getOne<Book>('books') 之类的东西,但我想防止这种情况发生(有关所需用法,请参阅 test() 函数)。

这不能用您想要的确切 API 来完成,因为函数在运行时无法访问您传递给函数的类型参数(以推断 table 名称)。您可以使用另一种方法,其中 interface 的名称是从集合名称中推断出来的,因此传递 'books' returns Book | null,传递 'films' returns Film | null,传递任何其他内容都是编译时错误。

type Collections = {
  books: Book,
  films: Film,
}

async function getOne<Name extends keyof Collections>(name: Name): Promise<Collections[Name] | null> {
  // fetch the actual value here
  return {} as Collections[Name];
}
  
async function test() {
  const book: Book | null = await getOne('books');
  console.log(book?.bookName);
  const film: Film | null = await getOne('films');
  console.log(film?.filmTitle);
  // Argument of type '"comments"' is not assignable to parameter of type 'keyof Collections'.
  await getOne('comments');
}

test();

Playground