18

In my typescript express app, I am trying to retrieve a document from mongoDB and then cast that document to a type defined by an interface.

const goerPostsCollection = databaseClient.client.db('bounce_dev1').collection('goerPosts'); var currentGoerPosts = await goerPostsCollection.findOne({ goerId: currentUserObjectId }) as GoerPosts; if (!currentGoerPosts) { currentGoerPosts = CreateEmptyGoerPosts(currentUserObjectId); } 

and the interface is defined like this

export interface GoerPosts { goerId: ObjectId; numPosts: number; posts: ObjectId[]; }; 

The code above has been working for me until I updated typescript from 4.4.4 to 4.5.2. Now the code no longer works and I get the error message:

[ERROR] 03:30:48 ⨯ Unable to compile TypeScript: [posts] src/routes/create-post.ts(37,28): error TS2352: Conversion of type 'WithId<Document> | null' to type 'GoerPosts' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. [posts] Type 'WithId<Document>' is missing the following properties from type 'GoerPosts': goerId, numPosts, posts 

I can probably just revert back to 4.4.4, but I am wondering if there is a better way to do this cleanly going forward.

2 Answers 2

35

I had an issue with this too. The toArray() function returns a WithId<Document> type. You can extend this interface to get typescript to work.

import type { WithId, Document } from 'mongodb' interface GoerPosts extends WithId<Document> { goerId: ObjectId; numPosts: number; posts: ObjectId[]; } var currentGoerPosts = (await goerPostsCollection.findOne({ goerId: currentUserObjectId }).toArray()) as GoerPosts; 

2022 Update

You can pass the type to the collection

export interface GoerPosts { goerId: ObjectId; numPosts: number; posts: ObjectId[]; }; const currentGoerPosts = databaseClient.client.db('bounce_dev1').collection<GoerPosts>('goerPosts').findOne({ goerId: currentUserObjectId }); 

This will return WithId<GoerPosts> without the need to extend the WithId interface

Sign up to request clarification or add additional context in comments.

3 Comments

I try the mongodb tutorial on typescript and get the same error as OP. When I change to your code and convert the class to an interface in the model the tsc error disappears. But why toArray() if you find one which is an object and not array?
You can pass the type to find() too if you've put the collection in a variable: const posts = goersPostsCollection.find<GoerPost>({}).toArray()
At least with project<Type> it appeared to be honor system type enforcement only. TypeScript acted like it was using my type, but the value returned was as if it were an any.
0

Adding upon @Trevor V answer, you can also define your collections globally by extending the mongoddb package types, and with some typescript magic you can get the types to be detected everywhere:

Create a globals.ts file and add the following

// globals.ts interface User { name: string; } interface Article { title: string; text: string; user: ObjectId; } export interface CollectionMap { // <collection-name>: Schema // add your collections here, eg: users: User; articles: Article; } declare module "mongodb" { interface Db { collection<K extends keyof CollectionMap>( collection: K, ): Collection<CollectionMap[K]>; } } 

Then just use it from another place:

// service.ts async function getArticle(db: Db) { const article = await db.collection("articles").findOne({ // article properties auto suggested here by editor }); // article is type of Article } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.