109

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?

3
  • 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. Commented Nov 21, 2013 at 4:16
  • 4
    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. Commented Nov 21, 2013 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. Commented Mar 15, 2017 at 2:38

11 Answers 11

140

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);
foo(xxx as any);
Sign up to request clarification or add additional context in comments.

9 Comments

This doesn't work: console.log("!!'false': ",!!'false') gives me !!'false': true
The string 'false' is a "truthy" in JavaScript (developer.mozilla.org/en-US/docs/Glossary/Truthy) so it works correctly.
@Toolkit, In javascript, and by extension Typescript, a string is only false if it is the empty string (or undefined).
@Knaģis what does mean !!xxx ?
@Roxy'Pro !!xxx literally means "not not xxx", so it's a double negation, effectively converting the expression "xxx" to a boolean.
|
19

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

2 Comments

Bad answer: isAdd is always true... (TS 2.9.1 here)
valueOf() is a no-op, since Boolean(x) will always return a boolean
11

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

2 Comments

Thank you, @fs_dm, that is actually much clearer than !!xxx.
the new Boolean(false) == true thing seems like a significant trip hazard to me to the point where i'll just use !! from now on. D:
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).

2 Comments

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.
Double casting seems long to write and junk to read, whereas !! is only obscure to really junior javascript developers.
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!'

Comments

5

Use this

YourMethod(!!isEnabled);

'!!' is used for type casting to boolean

1 Comment

Consider adding some explanation along with your code.
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);
        });
    })
});

Comments

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 ...
  }
}

2 Comments

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 That is true, but that is also true of Javascript, so I don't quite understand the problem. "false" is a truthy value.
0

Not as concise as !!foo, but it works and is pretty obvious.

(foo as any) as boolean

It is a mystery why typescript doesn't simply support 'as boolean' or to convert truthy to true and falsey to false. I believe typescript should support one or the both of the following:

foo as boolean
<boolean>foo

That type coercion is pretty explicit, and I don't think the transpiler needs to complain that maybe I didn't intend 'as boolean.'

Comments

0
const booleanOfsomeVal = someVal == "1" || someVal == "true";

This should work in typescript & javascript both

considering someVal could have possible values

0/1/"0"/"1"/true/false/"true"/"false"/undefined/null/NaN

Comments

-1

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;
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.