I want to write a static getOne<T>()
class method (i.e. it does not require a class instance or the new
operator to use it) which is also generic, and which returns MongoDB objects as an Item
which is either a Book
or a Film
. This is my initial idea, but now I don't know how to dynamically use the 'books'
or 'films'
collection name strings in place 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();
Is this even possible? I realise I could workaround this passing it as an argument with something like await getOne<Book>('books')
, but I want to prevent that (see the test()
function for the desired usage).