98

In Typescript I can do this:

var xxx : some_type;

if (xxx)
    foo();
else
    bar();

Here xxx will be treated as a boolean, regardless of its type.

I would like to do the same thing in a function argument. I have this function:

function foo(b : boolean) { ... }

I want to be able to call foo(xxx) and have xxx treated as a boolean, regardless of its type. But Typescript won't allow that.

I tried this:

foo(<boolean>xxx);

but that Typescript won't allow that either.

I can do this:

foo(xxx ? true : false);

But that seems a bit silly. Is there a better way to do it?

oz1cz
  • 5,504
  • 6
  • 38
  • 58
  • 2
    What you want to do is loose typing based in truthy and falsy logic. TypeScript's main purpose is to add static typing to JavaScript, precisely to prevent you from doing loose typing. So you're essentially trying to do something that TypeScript is designed to prevent. I suggest analyzing what types your passed parameter can be and put in overloaded definitions. – Stephen Chung Nov 21 '13 at 04:16
  • 3
    The issue here is consistency. If TypeScript is happy to let me use a non-boolean in `if (xxx)`, there is little reason that I shouldn't be able to use a non-boolean in other boolean contexts. In my opinion, it should either allow both or prohibit both. – oz1cz Nov 21 '13 at 13:18
  • 1
    Typescript is not "happy" about it, Typescript is backward compatible with JavaScript. Think of "if" as a function taking a parameter typed as Any. This is also why your ternary expression works. TS discourages this kind of sloppy typing but tolerates it for backward compatibility. A below answer using `!!` works because the input of the not operator is Any and the output is Boolean. – Peter Wone Mar 15 '17 at 02:38

9 Answers9

129

You can use double exclamation sign trick which Typescript does allow and which works fine in JavaScript:

foo(!!xxx);

Alternatively, cast it to any

foo(<any>xxx);
Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • The first would actually convert it to boolean though, I'm guessing OP wants to _usually_ pass a type but wants to be able to pass boolean for testing for example. – Benjamin Gruenbaum Nov 20 '13 at 10:47
  • 8
    This doesn't work: console.log("!!'false': ",!!'false') gives me !!'false': true – Toolkit Dec 14 '16 at 09:34
  • 30
    The string 'false' is a "truthy" in JavaScript (https://developer.mozilla.org/en-US/docs/Glossary/Truthy) so it works correctly. – Knaģis Dec 14 '16 at 13:09
  • 7
    @Toolkit, In javascript, and by extension Typescript, a string is only false if it is the empty string (or undefined). – daotoad Apr 28 '17 at 01:10
  • 1
    @Knaģis what does mean !!xxx ? – Roxy'Pro Aug 16 '18 at 11:19
  • Another simple & clear solution, see this answer using the JavaScript Boolean() function: https://stackoverflow.com/questions/31155477/cleanest-way-to-convert-to-boolean – Giant Elk Sep 17 '19 at 01:53
  • 2
    @Roxy'Pro `!!xxx` literally means "not not xxx", so it's a double negation, effectively converting the expression "xxx" to a boolean. – Nathan Arthur Oct 21 '19 at 14:24
  • To expand on @Knaģis' comment, if getting a boolean value from a stringified boolean is the issue (and you're sure it's always going to be "true" or "false"), then `xxx === "true"` will get the correct boolean value. If it could be a string or a boolean, then something like `[true, "true"].includes(xxx)` would work. The latter approach can easily be extended to allow for any potential "truthy" values (like `1` or `"1"` if reading from env vars) or if you want to re-cast falsey values (like `''` or `0`) as `true`, this will allow for that as well (though that last bit is rarely a good idea). – Scott Schupbach Jul 10 '20 at 18:34
18

Here is my solution for "typescript": "^3.3.3":

function toBool(a: any) {
  return Boolean(a).valueOf();
}

export { toBool };

unit test:

import { toBool } from '../../utils/bool';

describe('bool', () => {
  describe('#toBool', () => {
    it('should convert string to boolean', () => {
      expect(toBool('false')).toBeTruthy();
      expect(toBool('')).toBeFalsy();
    });
    it('should convert number to boolean', () => {
      expect(toBool(1)).toBeTruthy();
      expect(toBool(0)).toBeFalsy();
      expect(toBool(-1)).toBeTruthy();
      expect(toBool(Infinity)).toBeTruthy();
      expect(toBool(-Infinity)).toBeTruthy();
    });
    it('should convert null to boolean', () => {
      expect(toBool(null)).toBeFalsy();
    });
    it('should convert undefined to boolean', () => {
      expect(toBool(undefined)).toBeFalsy();
    });
    it('should convert NaN to boolean', () => {
      expect(toBool(NaN)).toBeFalsy();
    });
    it('should convert object to boolean', () => {
      expect(toBool({})).toBeTruthy();
    });
    it('should convert array to boolean', () => {
      expect(toBool([])).toBeTruthy();
    });
  });
});

unit test results:

 PASS  src/__tests__/utils/bool.spec.ts
  bool
    #toBool
      ✓ should convert string to boolean (3ms)
      ✓ should convert number to boolean (1ms)
      ✓ should convert null to boolean (1ms)
      ✓ should convert undefined to boolean
      ✓ should convert NaN to boolean (1ms)
      ✓ should convert object to boolean (1ms)
      ✓ should convert array to boolean

Test Suites: 1 passed, 1 total
Tests:       7 passed, 7 total
Snapshots:   0 total
Time:        3.79s, estimated 4s
Lin Du
  • 88,126
  • 95
  • 281
  • 483
9

The most obvious way to do this with typescript is to use a Boolean constructor:

Boolean(someVal);

in your case it will be:

foo(Boolean(xxx));

please note that the constructor is used without the "new" keyword. Because if you add it, you will create a new boolean object instead of casting the value:

Boolean(false) == false 

but

new Boolean(false) == true

because it is an object

fs_dm
  • 391
  • 3
  • 13
8

While you can't cast a number directly to a boolean, you can cast it to the wrapper Boolean class and immediately unwrap it. For example:

foo(<boolean><Boolean>xxx);

While clumsy, it avoids the type erasure of casting to <any>. It's also arguably less obscure & more readable than the !! approach (certainly so in the transpiled js code).

dug
  • 2,275
  • 1
  • 18
  • 25
  • 1
    Helpful answer, thanks. But I like to add that I've seen (and used) the `!!` approach in many places in javascript and typescript, so I don't think the "obscure" argument is very solid. I personally think it is more readable than double-cast. – Iravanchi Apr 16 '15 at 15:47
  • 4
    Double casting seems long to write and junk to read, whereas `!!` is only obscure to really junior javascript developers. – Teodor Sandu Feb 13 '17 at 16:35
8

With TypeScript 2.0.2 you can do this:

type Falsey = '' | 0 | false | null | undefined;

function eatFruit(fruit: string | Falsey) { 
  if (fruit) {
    alert(`Ate ${fruit}`);
  } else {
    alert('No fruit to eat!');
  }
}

const fruits = ['apple', 'banana', 'pear'];
eatFruit(fruits[0]); // alerts 'Ate apple'
eatFruit(fruits[1]); // alerts 'Ate banana'
eatFruit(fruits[2]); // alerts 'Ate pear'
eatFruit(fruits[3]); // alerts 'No fruit to eat!'

const bestBeforeDay = 12;
let day = 11;
eatFruit(day < bestBeforeDay && 'peach'); // alerts 'Ate peach'
day += 1;
eatFruit(day < bestBeforeDay && 'peach'); // alerts 'No fruit to eat!'

let numMangos = 1;
eatFruit(numMangos && 'mango'); // alerts 'Ate Mango'
numMangos -= 1;
eatFruit(numMangos && 'mango'); // alerts 'No fruit to eat!'
benlambert
  • 111
  • 1
  • 3
5

Use this

YourMethod(!!isEnabled);

'!!' is used for type casting to boolean

Khabir
  • 5,370
  • 1
  • 21
  • 33
Abdus Salam Azad
  • 5,087
  • 46
  • 35
4

Here's a simple function that will handle most scenarios, including handling booleans as an input (just in case):

type Falsey = undefined | null;
const parseBoolean = (val: string | boolean | number | Falsey): boolean => {
  const s = val && val.toString().toLowerCase().trim();
  if (s == 'true' || s == '1')
    return true;
  return false; 
}

And here's a jest test to go with it:

describe('Boolean Parser', () => {
    [
        { val: 'true', expected: true },
        { val: 'false', expected: false },
        { val: 'True', expected: true },
        { val: 'False', expected: false },
        { val: 'TRUE', expected: true },
        { val: 'FALSE', expected: false },
        { val: '', expected: false },
        { val: '1', expected: true },
        { val: '0', expected: false },
        { val: false, expected: false },
        { val: true, expected: true },
        { val: undefined, expected: false },
        { val: null, expected: false },
        { val: 0, expected: false },
        { val: 1, expected: true },
        { val: 111, expected: false }
    ].forEach(args => {
        it(`should parse ${args.val} to boolean`, () => {
            expect(parseBoolean(args.val)).toBe(args.expected);
        });
    })
});
asusfan
  • 41
  • 2
3
foo(!!xxx); // This is the common way of coercing variable to booleans.
// Or less pretty
foo(xxx && true); // Same as foo(xxx || false)

However, you will probably end up duplicating the double bang everytime you invoke foo in your code, so a better solution is to move the coerce to boolean inside the function DRY

foo(xxx);

foo(b: any){
  const _b = !!b;
  // Do foo with _b ...
}
  /*** OR ***/
foo(b: any){
  if(b){
    // Do foo ...
  }
}
  • As stated above: non-empty strings are not going to magically be cast to their boolean equivalent if ("false") { console.info("I'm not false"); } // this will log – Gyuri Sep 06 '18 at 21:20
0

if(xxx) {...} //read as TRUE if xxx is NOT undefined or null if(!xxx) {...} //read as TRUE if xxx IS undefined or null

For a string like 'true' or 'false': xxx.toLowerCase().trim() === 'true' ? true : false

so:

var zzz = 'true'; //string
var yyy = [];  //array

...

if(zzz.toLowerCase().trim() === 'true') { ... }  // quick string conversion

...

if(yyy ? true : false) { ... }  // quick any conversion - it's TRUE if it's not null or undefined

...

// in a catch-all function

if(toBoolean(zzz)) { ... }
if(toBoolean(yyy)) { ... }


toBoolean(xxx: any): boolean {
  if(xxx) {
    const xStr = xxx.toString().toLowerCase().trim();
    if(xStr === 'true' || x === 'false') {
      return xStr === 'true' ? true : false;
    } else {
      return xxx ? true : false;
    }
  } else {
    return false;
  }
}
Lubo
  • 11