Search code examples
javascriptnestjsprisma

How to list properties of a Nestjs DTO class?


I have following Nestjs DTO class:

// create-job-offer.dto.ts
import { IsOptional, IsNumber } from 'class-validator';

export class CreateJobOfferDto {
  @IsNumber()
  @IsOptional()
  mentorId: number;

  @IsNumber()
  @IsOptional()
  companyId: number;
}

I want to obtain the list of class properties: ['mentorId', 'companyId'].

I tried so far in a controller without success following methods:

Object.getOwnPropertyNames(new CreateJobOfferDto());
Object.getOwnPropertyNames(CreateJobOfferDto);

Object.getOwnPropertySymbols(new CreateJobOfferDto());
Object.getOwnPropertySymbols(CreateJobOfferDto);

Object.getOwnPropertyDescriptors(CreateJobOfferDto);
Object.getOwnPropertyDescriptors(new CreateJobOfferDto());

Object.getPrototypeOf(CreateJobOfferDto);
Object.getPrototypeOf(new CreateJobOfferDto());

If I add a method, or vars in a constructor, I can get them, but not the properties.

The reason why I want to achieve this is, I am using Prisma and React, and in my React app I want to receive the list of class properties so that I can generate a model form dynamically.


Solution

  • There is no easy direct way to get the list of properties of a DTO class, or more comprehensively, any type of class that has only properties.

    The reason is that whenever you defined a property without any values, it becomes disappears after compiling to javascript.

    Example:

    // typescript
    class A {
        private readonly property1: string;
        public readonly property2: boolean;
    }
    

    it compiles to this:

    // javascript
    "use strict";
    class A {}
    

    In order to achieve this goal, you need to write a custom decorator. like this:

    const properties = Symbol('properties');
    
    // This decorator will be called for each property, and it stores the property name in an object.
    export const Property = () => {
      return (obj: any, propertyName: string) => {
        (obj[properties] || (obj[properties] = [])).push(propertyName);
      };
    };
    
    // This is a function to retrieve the list of properties for a class
    export function getProperties(obj: any): [] {
      return obj.prototype[properties];
    }
    
    

    Imagine I have a DTO class for a user, like this:

    import { getProperties } from './decorators/property.decorator';
    
    export class UserDto {
      @Property()
      @IsNotEmpty()
      firstName: string;
    
      @Property()
      @IsEmail()
      @IsOptional()
      public readonly email: string;
    }
    

    and I want to get all properties in a list, so we need to call the getProperties method which we defined earlier, so:

    import { UserDto } from './dtos/user.dto';
    
    getProperties(UserDto); // [ 'firstName', 'email' ]
    

    You can also use an npm module ts-transformer-keys.

    A TypeScript custom transformer which enables to obtain keys of given type.

    How to use:

    import { keys } from 'ts-transformer-keys';
    
    interface Props {
      id: string;
      name: string;
      age: number;
    }
    const keysOfProps = keys<Props>();
    
    console.log(keysOfProps); // ['id', 'name', 'age']