Typescript - convert type of mongoose ObjectId to string when sharing between backend and frontend

Viewed 15

I am trying to create few types based on mongo schema models and sharing the same with front end in mono repo.

Example type:

import { Types } from "mongoose";

export interface Profile {
  userId: Types.ObjectId;
  education: {
    university: Types.ObjectId;
  };
}

My Profile Schema use this Profile interface to define types and the same interface is used in front end. Since front end does not (need to) know about mongoose types, I would like to have the userId and education.university as string. Btw when I get api response from server in front end I get these values as string only.

I have tried a TS mapper as beautifully defined here. This solved my problem partially - I can see userId being converted to string in front end. But this does not solve the nested object education. I can define education as separate type and use the mapper, but I think this is not a scalable way. I would like to have the mapper to handle the type conversion of the nested types as well.

typeMapper.ts

import { Types } from "mongoose";

type MapObjectIdToString<PropType> = PropType extends Types.ObjectId
  ? string
  : PropType;

  export type MapDbObject<T> = {
    [PropertyKey in keyof T]: MapObjectIdToString<T[PropertyKey]>;
  };

I am converting Profile type like this

export type UiProfile = MapDbObject<Profile>; // here `userId` is string, but `university` is still Types.ObjectId

Is there a way to update my TS Mapper to convert all fields including fields inside nested object from Types.ObjectId to string? Thanks in advance.

1 Answers

A recursive type will do it:

type StringIds<T> = T extends Types.ObjectId ? string : T extends Record<any, any> ? {
    [K in keyof T]: StringIds<T[K]>;
} : T;

Also works with arrays. Try it yourself below:

Playground

Related