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).
This cannot be done with the exact API you want since the type parameters you pass into a function cannot be accessed by the function at runtime (to infer the table name). You can use another approach where the name of the
interface
is inferred from the collection name so that passing'books'
returnsBook | null
, passing'films'
returnsFilm | null
, and passing anything else is a compile time error.Playground