skip to Main Content

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


  1. Chosen as BEST ANSWER

    So I tried my luck with ChatGPT and it came up with with this:

    export type WithStringId<T> = T & {_id: string};
    

    Which meets all my requirements


  2. Try

    type WithStringId<Schema> = Schema extends {
      _id: string
    } ? Schema : never
    

    The never type will simply not be allowed by the compiler.

    Login or Signup to reply.
  3. 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 intersection ObjectId & 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:

    export type WithStringId<T> = Omit<T, '_id'> & { _id: string }
    

    Now, the _id is strictly string.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search