Skip to content

Fix boolean conversion from string values when enableImplicitConversion option is set to true #1329

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/TransformOperationExecutor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defaultMetadataStorage } from './storage';
import { ClassTransformOptions, TypeHelpOptions, TypeMetadata, TypeOptions } from './interfaces';
import { TransformationType } from './enums';
import { getGlobal, isPromise } from './utils';
import { getGlobal, isPromise, toBoolean } from './utils';

function instantiateArrayType(arrayType: Function): Array<any> | Set<any> {
const array = new (arrayType as any)();
Expand Down Expand Up @@ -106,8 +106,9 @@ export class TransformOperationExecutor {
if (value === null || value === undefined) return value;
return Number(value);
} else if (targetType === Boolean && !isMap) {
if (value === null || value === undefined) return value;
return Boolean(value);
// Note: Next line can safely be removed as this is being handled by util to return false if either of these two are set.
//if (value === null || value === undefined) return value;
return toBoolean(value);
} else if ((targetType === Date || value instanceof Date) && !isMap) {
if (value instanceof Date) {
return new Date(value.valueOf());
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './get-global.util';
export * from './is-promise.util';
export * from './to-boolean.util';
29 changes: 29 additions & 0 deletions src/utils/to-boolean.util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export function toBoolean(value: any): value is Boolean {
if (value === undefined || value === null) {
return false;
}
if (typeof value === 'boolean') {
return value;
}
if (typeof value === 'number') {
return value > 0;
}
if (typeof value === 'string') {
value = value.toLowerCase();
switch (value) {
case 'true':
case 'yes':
case 'on':
case '1':
return true;
case 'false':
case 'no':
case 'off':
case '0':
return false;
default:
return false;
}
}
return false;
}
12 changes: 6 additions & 6 deletions test/functional/basic-functionality.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand All @@ -646,7 +646,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand All @@ -664,7 +664,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand All @@ -682,7 +682,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand All @@ -700,7 +700,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand All @@ -720,7 +720,7 @@ describe('basic functionality', () => {
uuidBuffer: uuid,
nullableString: null,
nullableNumber: null,
nullableBoolean: null,
nullableBoolean: false,
nullableDate: null,
nullableBuffer: null,
});
Expand Down
96 changes: 96 additions & 0 deletions test/functional/implicit-type-declarations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,99 @@ describe('plainToInstance transforms built-in primitive types properly', () => {
expect(result.boolean2).toBeFalsy();
});
});

describe('plainToInstance transforms boolean types properly', () => {
defaultMetadataStorage.clear();

class Example {
@Type()
undefinedValue: boolean;

@Type()
nullValue: boolean;

@Type()
booleanTrueString: boolean;

@Type()
booleanFalseString: boolean;

@Type()
trueNumberStringValue: boolean;

@Type()
falseNumberStringValue: boolean;

@Type()
boolean: boolean;

@Type()
boolean2: boolean;

@Type()
boolean3: boolean;

@Type()
boolean4: boolean;

@Type()
onTrueString: boolean;

@Type()
offFalseString: boolean;

@Type()
booleanTrueNumber: boolean;

@Type()
booleanFalseNumber: boolean;

@Type()
falseRandomString: boolean;
}

const result: Example = plainToInstance(
Example,
{
undefinedValue: undefined,
nullValue: null,
booleanTrueString: 'true',
booleanFalseString: 'false',
trueNumberStringValue: '1',
falseNumberStringValue: '0',
boolean: 1,
boolean2: 0,
boolean3: true,
boolean4: false,
onTrueString: 'on',
offFalseString: 'off',
booleanTrueNumber: '1',
booleanFalseNumber: '0',
falseRandomString: 'some random value that needs to be false',
},
{ enableImplicitConversion: true }
);
it('should recognize and convert "undefined" and "null" to false', () => {
expect(result.undefinedValue).toBeFalsy();
expect(result.nullValue).toBeFalsy();
});

it('should recognize and convert string to boolean', () => {
expect(result.booleanTrueString).toBeTruthy();
expect(result.booleanFalseString).toBeFalsy();
expect(result.trueNumberStringValue).toBeTruthy();
expect(result.falseNumberStringValue).toBeFalsy();
expect(result.onTrueString).toBeTruthy();
expect(result.offFalseString).toBeFalsy();
expect(result.booleanTrueNumber).toBeTruthy();
expect(result.booleanFalseNumber).toBeFalsy();
expect(result.falseRandomString).toBeFalsy();
});

it('should recognize and convert to boolean', () => {
expect(result.boolean).toBeTruthy();
expect(result.boolean2).toBeFalsy();
expect(result.boolean3).toBeTruthy();
expect(result.boolean4).toBeFalsy();
});
});