class-transformer icon indicating copy to clipboard operation
class-transformer copied to clipboard

feature: ship common transforms in subdirectory

Open ceopaludetto opened this issue 5 years ago • 7 comments

Description

Due to the deprecation of the class-sanitizer package, release common transformations will improve new users experience.

Proposed solution

I've imagined something like:

import { Trim, ToLowerCase } from 'class-transformer/common'

class Person {
  @Trim()
  @ToLowerCase()
  public email!: string;
}

Thus this can be achieved with this piece of code:

import { Trim } from 'class-transformer'

export function Trim() {
  return (target: any, key: string) => {
    return Transform((value: string) => value.trim())(target, key);
  };
}

Or in a more customizable way:

export function Trim(only?: "left" | "right") {
  return (target: any, key: string) => {
    return Transform((value: string) => {
      if (only) {
        if (only === "left") {
          return value.trimLeft();
        }

        return value.trimRight();
      }

      return value.trim();
    })(target, key);
  };
}

If the solution can't be shipped, encourage new users to create your own decorators to avoid mismatch between yours dtos.

Thanks for pay attention, awesome work!

ceopaludetto avatar Aug 03 '20 21:08 ceopaludetto

Yes, this is on the roadmap but we need to figure out what is the best way to ship this. In the meantime feel free to use class-sanitizer the latest version is not deprecated just new development is halted. (I will deprecate only if a security bug arise.)

NoNameProvided avatar Aug 04 '20 19:08 NoNameProvided

ping, want this too.

ruscon avatar Feb 18 '21 12:02 ruscon

Better solution

import { Transform, TransformOptions } from 'class-transformer';

export interface TrimOptions {
    /** @default 'both' */
    strategy?: 'start' | 'end' | 'both';
}

export function Trim(options?: TrimOptions, transformOptions?: TransformOptions): (target: any, key: string) => void {
    return Transform((value: unknown) => {
        if ('string' !== typeof value) {
            return value;
        }

        switch (options?.strategy) {
            case 'start':
                return value.trimLeft();
            case 'end':
                return value.trimRight();
            default:
                return value.trim();
        }
    }, transformOptions);
}

Test

import { plainToClass } from 'class-transformer';

describe('#Trim', () => {
    class TestModel {
        @Trim()
        withDefault: string;
        @Trim({ strategy: 'start' })
        trimStart: string;
        @Trim({ strategy: 'end' })
        trimEnd: string;
        @Trim({ strategy: 'both' })
        trimBoth: string;
    }

    it('should transform with success', async () => {
        return plainToClass(TestModel, {
            withDefault: '   withDefault   ',
            trimStart: '   trimStart   ',
            trimEnd: '   trimEnd   ',
            trimBoth: '   trimBoth   ',
        }).should.eql({
            withDefault: 'withDefault',
            trimStart: 'trimStart   ',
            trimEnd: '   trimEnd',
            trimBoth: 'trimBoth',
        });
    });
});

ruscon avatar Feb 18 '21 13:02 ruscon

I'm using NestJs, and we love decorators. This is working so good :

import {
  Transform,
  TransformFnParams,
  TransformOptions,
} from "class-transformer";

export interface TrimOptions {
  /** @default 'both' */
  strategy?: "start" | "end" | "both";
}

export function TrimOrNull(
  options?: TrimOptions,
  transformOptions?: TransformOptions
): (target: any, key: string) => void {
  return Transform((sourceData: TransformFnParams) => {
    if ("string" !== typeof sourceData.value) {
      return null;
    }

    switch (options?.strategy) {
      case "start":
        return sourceData.value.trimStart();
      case "end":
        return sourceData.value.trimEnd();
      default:
        return sourceData.value.trim();
    }
  }, transformOptions);
}

pYassine avatar May 09 '22 12:05 pYassine

Any update on this?

moshest avatar Jun 29 '22 11:06 moshest

Would love to see common transform decorators!

eddienubes avatar Jul 12 '22 11:07 eddienubes

Better solution for version >= v0.4.0

-     return Transform((value: unknown) => {
+     return Transform(({ value }: any) => {
import { Transform, TransformOptions } from 'class-transformer';

export interface TrimOptions {
    /** @default 'both' */
    strategy?: 'start' | 'end' | 'both';
}

export function Trim(options?: TrimOptions, transformOptions?: TransformOptions): (target: any, key: string) => void {
    return Transform(({ value }: any) => {
        if ('string' !== typeof value) {
            return value;
        }

        switch (options?.strategy) {
            case 'start':
                return value.trimLeft();
            case 'end':
                return value.trimRight();
            default:
                return value.trim();
        }
    }, transformOptions);
}

KeithYeh avatar Mar 31 '23 09:03 KeithYeh