The WithId
generic that comes with Mongo’s typescript library is highly useful in scenarios where you don’t want to modify your existing interface/type for use with MongoDB such as:
export class TagsCl {
private publicTags: Promise<Array<WithId<ProductTag>>>;
private allTags: Promise<Array<WithId<ProductTag>>>;
private tags: Collection<ProductTag>;
constructor(){
//...
}
//...
}
I’d like a very similar generic called WithStrId
or WithStringId
or whatever that accomplishes the same thing, but adds a field with _id: string
instead to an existing type/interface, for use in the front-end. What’s the easiest way to make such a generic?
The existing WithId
looks a bit complicated
/** TypeScript Omit (Exclude to be specific) does not work for objects with an "any" indexed type, and breaks discriminated unions @public */
export declare type EnhancedOmit<TRecordOrUnion, KeyUnion> = string extends keyof TRecordOrUnion ? TRecordOrUnion : TRecordOrUnion extends any ? Pick<TRecordOrUnion, Exclude<keyof TRecordOrUnion, KeyUnion>> : never;
export declare type InferIdType<TSchema> = TSchema extends {
_id: infer IdType;
} ? Record<any, never> extends IdType ? never : IdType : TSchema extends {
_id?: infer IdType;
} ? unknown extends IdType ? ObjectId : IdType : ObjectId;
export declare type WithId<TSchema> = EnhancedOmit<TSchema, '_id'> & {
_id: InferIdType<TSchema>;
};
3
Answers
So I tried my luck with ChatGPT and it came up with with this:
Which meets all my requirements
Try
The
never
type will simply not be allowed by the compiler.I started with x0a’s answer and it works well, but when I apply it to types that already have
_id: ObjectId
the resulting type of _id becomes the intersectionObjectId & string
. Since I’m only going to use this type on the frontend I added an Omit to strip the _id type information before adding it back:Now, the _id is strictly
string
.