diff --git a/.travis.yml b/.travis.yml index 56706be20..46a23d444 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - stable + - 10 - 8 after_success: diff --git a/README.md b/README.md index 5ff46afc7..d6b878237 100644 --- a/README.md +++ b/README.md @@ -245,6 +245,33 @@ import {deserializeArray} from "class-transformer"; let photos = deserializeArray(Photo, photos); ``` +## Type casting + +By default typescript emits types metadata and we can infer how to cast them. But `Boolean` works not as expected + + +```typescript +Boolean('false') === true +Boolean(undefined) === false +``` +So there is an opportunity to cast boolean as expected: + +```typescript +class Dto { + @ToBoolean() + hasFlag: boolean; +} + +plainToClass(Dto, {hasFlag: 'false'}) +// { hasFlag: false } +plainToClass(Dto, {hasFlag: 0}) +// { hasFlag: false } +plainToClass(Dto, {hasFlag: 1}) +// { hasFlag: true } +``` + + + ## Enforcing type-safe instance The default behaviour of the `plainToClass` method is to set *all* properties from the plain object, diff --git a/src/decorators.ts b/src/decorators.ts index 6762d76d7..38a3eee04 100644 --- a/src/decorators.ts +++ b/src/decorators.ts @@ -18,6 +18,24 @@ export function Transform(transformFn: (value: any, obj: any, transformationType }; } + +/** + * Cast value to boolean type + */ +export function ToBoolean() { + return Transform((value) => { + if (value === "true" || value === "1" || value === 1) { + return true; + } + + if (value === "false" || value === 0 || value === "0") { + return false; + } + + return value; + }); +} + /** * Specifies a type of the property. * The given TypeFunction can return a constructor. A discriminator can be given in the options. diff --git a/test/functional/to-boolean-decorator.spec.ts b/test/functional/to-boolean-decorator.spec.ts new file mode 100644 index 000000000..eb4716bf2 --- /dev/null +++ b/test/functional/to-boolean-decorator.spec.ts @@ -0,0 +1,58 @@ +import {plainToClass, ToBoolean} from "../../src"; +import { expect } from "chai"; + +describe("Cast to boolean", () => { + class Dto { + @ToBoolean() + hasFlag: boolean; + } + + it("Should cast false value as false", () => { + + const result = plainToClass(Dto, { hasFlag: "false" }); + expect(result.hasFlag).to.be.false; + }); + + it("Should cast true value as true", () => { + const result = plainToClass(Dto, { hasFlag: "true" }); + expect(result.hasFlag).to.be.true; + }); + + it("Should not cast undefined as bool", () => { + const result = plainToClass(Dto, { hasFlag: undefined }); + expect(result.hasFlag).to.not.be.equals(true); + expect(result.hasFlag).to.not.be.equals(false); + }); + + it("Should not cast null as bool", () => { + const result = plainToClass(Dto, { hasFlag: null }); + expect(result.hasFlag).to.not.be.equals(true); + expect(result.hasFlag).to.not.be.equals(false); + }); + + it("Should cast 0 as false", () => { + const result = plainToClass(Dto, { hasFlag: 0 }); + expect(result.hasFlag).to.be.equals(false); + }); + + it("Should cast 1 as true", () => { + const result = plainToClass(Dto, { hasFlag: 1 }); + expect(result.hasFlag).to.be.equals(true); + }); + + it("Should cast '0' as false", () => { + const result = plainToClass(Dto, { hasFlag: "0" }); + expect(result.hasFlag).to.be.equals(false); + }); + + it("Should not cast '1' as true", () => { + const result = plainToClass(Dto, { hasFlag: "1" }); + expect(result.hasFlag).to.be.equals(true); + }); + + it("Should remain real bool", () => { + const result = plainToClass(Dto, { hasFlag: true }); + expect(result.hasFlag).to.be.equals(true); + }); + +});