Lets make Construction illegal in TS


I was looking into the Novu open-source project. It follows the DDD pattern for development. Over there, I saw the common DDD pattern with command and use-case implementations over the NestJS service.

Photo by Jeriden Villegas on Unsplash

Command in Domain Driven Development is basically like DTO(a simple class in layman term). I came across to see that every command had followed ClassName.create() pattern to create a new class instead of using constructors.

The static create method in the command handled all the validations. So, it was logical to make the constructor illegal to use, which drove me into implementing something like this in typescript.

In simple words, the code below should make the constructor illegal to use in the type level. Forcing users to use ClassName.create() method instead.

We can also make it asynchronous as well.

// Happy Nepali New Year 2080 guys 🇳🇵


// this is me mocking a database repository level 
const carsDb = {
  exists:async (id:string)=>Promise.resolve(false),
  getUser: async(id:string)=>Promise.resolve({
    "name":"Nirjal Paudel"
  })
}



/*
Lets say you have a use case, where
you want to instaniate an object if and 
only if your some asynchronous API call works
*/
class Car{  
  id: string;

  // I am lazy to make types here hehe
  user: any; 

  // This will make constuctor illegal to use in type level
  private constructor(id:string){
    this.id = id
  }

  static async createCar(id:string){
    // some asyncronous business logic here
    // lets say you create check an API here to find out
    // if car by the id exists or not already,
    const carExists = await carsDb.exists(id);
    if (!carExists) throw new Error("Cannot create Car");
    const carObj = new Car(id);
    // some wrapping around car -> lets say
    carObj.user = await carsDb.getUser(id);
    return carObj;
  }


}

// should give type error
const car1 = new Car("car1-id");
const car2 = new Car()

// works as an asynchronous constructor
const car2 = await Car.createCar("car2-id");


// making it modular to support top level await
export {}

Novu: https://github.com/novuhq/novu

#opensource #development