My goal is to create a generic and static class method called getOne<T>()
that can return MongoDB objects as an Item
, where the Item
can be either a Book
or a Film
. Initially, I had an idea on how to achieve this, but now I am struggling with dynamically using the collection names 'books'
or 'films'
instead of '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();
My question is, is it possible to achieve this without passing the collection name as an argument in the function call (e.g. getOne<Book>('books')
)? I want to avoid that and maintain the desired usage shown in the test()
function.