Quiero combinar varias clases para la reutilización y la coherencia entre el backend y el frontend. Algo como:
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());¿Es posible algo similar de todos modos?
Nota: No me refiero a tipos solo como TypeScript's & . El objetivo principal es reutilizar la validación de clases.
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; }Hay una forma realmente ingeniosa de hacer esto usando el patrón mixin .
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());usando esta utilidad:
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) ); }); }); }Gracias a Filip Kaštovský que me indicó este documento: ¿Cómo funciona un Mixin ?
Tengo que resolverlo de esta manera:
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());EDITAR: Cloné @nestjs/mapped-types y lo modifiqué para que funcione tanto en el navegador como en nodejs. Quería publicarlo, pero no sé mucho acerca de la publicación y si realmente va a funcionar. Funciona bien en mi configuración con Nx porque usé Nx para crear la biblioteca .
¿Puede esto lograr lo que necesitas?
Class User{ userEmail: UserEmail; userUserName: UserUserName; userPassword: UserPassword; }