Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

149
Vistas
JavaScript: Merge multiple classes

I want to combine multiple classes for reusability and consistency amongst backend and frontend. Something like:

import {
  IsEmail,
  IsString,
  MaxLength,
  MinLength,
  validateSync
} from "class-validator";

class UserUsername {
  @IsString()
  @MinLength(3)
  @MaxLength(10)
  username!: string;
}

class UserEmail {
  @IsEmail()
  email!: string;
}

class UserPassword {
  @IsString()
  @MinLength(8)
  password!: string;
}

class UserSecret {
  @IsString()
  secret!: string;
}

class User /* extends UserEmail, UserUsername, UserSecret */ {}
class UserDto /* extends UserEmail, UserUsername, UserPassword */ {}

const userDto = new UserDto();

userDto.username = "noerror";
userDto.email = "error";
userDto.password = "error";

console.log(validateSync(userDto).toString());

Is something similar anyway possible?

Note: I do not mean types only like TypeScript's &. The main purpose is to reuse class validation.

Link to sandbox

The Problem:

export class User {
  @IsUUID()
  id!: string;

  @IsNotEmpty()
  @IsString()
  @MinLength(3)
  username!: string;

  @IsEmail()
  email!: string;

  @IsNotEmpty()
  @IsString()
  secret!: string;

  @IsNotEmpty()
  @IsAlpha()
  firstName!: string;

  @IsNotEmpty()
  @IsAlpha()
  lastName!: string;

  @IsBoolean()
  isEmailVerified!: boolean;
}

export class UserDto {
  @IsUUID()
  id!: string;

  @IsNotEmpty()
  @IsString()
  @MinLength(3)
  username!: string;

  @IsEmail()
  email!: string;

  secret?: never;

  @IsNotEmpty()
  @IsAlpha()
  firstName!: string;

  @IsNotEmpty()
  @IsAlpha()
  lastName!: string;

  @IsBoolean()
  isEmailVerified!: boolean;
}

export class SignUpUserDto {
  id?: never;

  @IsNotEmpty()
  @IsString()
  @MinLength(3)
  username!: string;

  @IsEmail()
  email!: string;

  secret?: never;

  @IsNotEmpty()
  @IsAlpha()
  firstName!: string;

  @IsNotEmpty()
  @IsAlpha()
  lastName!: string;

  isEmailVerified?: never;
}

export class UpdateUserDto {
  id?: never;

  @IsOptional()
  @IsNotEmpty()
  @IsString()
  @MinLength(3)
  username?: string;

  @IsOptional()
  @IsEmail()
  email?: string;

  secret?: never;

  @IsOptional()
  @IsNotEmpty()
  @IsAlpha()
  firstName?: string;

  @IsOptional()
  @IsNotEmpty()
  @IsAlpha()
  lastName?: string;

  isEmailVerified?: never;
}

export class SignInUserDto {
  @IsString()
  @IsNotEmpty()
  username!: string;

  @IsNotEmpty()
  @IsString()
  password!: string;
}

See how repetitive the code is?

about 4 years ago · Juan Pablo Isaza
3 Respuestas
Responde la pregunta

0

There is a really neat way of doing this using the mixin pattern.

class UserDto {}
interface UserDto extends UserEmail, UserUsername, UserPassword {}
applyMixins(UserDto, [UserEmail, UserUsername, UserPassword]);

const userDto = new UserDto();

userDto.username = 'noerror';
userDto.email = 'error';
userDto.password = 'error';

console.log(validateSync(userDto).toString());

using this util:

function applyMixins(derivedCtor: any, constructors: any[]) {
    constructors.forEach((baseCtor) => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
            Object.defineProperty(
                derivedCtor.prototype,
                name,
                Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
                    Object.create(null)
            );
        });
    });
}
about 4 years ago · Juan Pablo Isaza Denunciar

0

Thanks to Filip Kaštovský who pointed me to this doc: How Does A Mixin Work?

I got to solve it this way:

import { IsString, IsUUID, MinLength, validateSync } from 'class-validator';

type Constructor<T = {}> = new (...args: any[]) => T;

export function WithUserId<TBase extends Constructor>(Base: TBase) {
  class UserId extends Base {
    @IsUUID()
    id!: string;
  }

  return UserId;
}

export function WithUserUsername<TBase extends Constructor>(Base: TBase) {
  class UserUsername extends Base {
    @IsString()
    @MinLength(3)
    username!: string;
  }

  return UserUsername;
}

export function WithUserSecret<TBase extends Constructor>(Base: TBase) {
  class UserSecret extends Base {
    @IsString()
    secret!: string;
  }

  return UserSecret;
}

class User extends WithUserId(WithUserUsername(WithUserSecret(class {}))) {}

const user = new User();

user.id = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";
user.username = "a";
user.secret = "a";

console.log(validateSync(user).toString());

Link to sandbox

EDIT: I cloned @nestjs/mapped-types and modified it to work in both the browser and nodejs. I wanted to publish it but I do not know a lot about publishing and whether it is actually going to work. It works fine in my set-up with Nx because I used Nx to create the library.

about 4 years ago · Juan Pablo Isaza Denunciar

0

Can this achieve what you need?

Class User{
   userEmail: UserEmail;
   userUserName: UserUserName;
   userPassword: UserPassword;
}
about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda