typescripttsctruthy

Type for non-false, aka truthy


In TypeScript, is there a type for truthy?

I have this method:

Object.keys(lck.lockholders).length;
    enqueue(k: any, obj?: any): void

I think with TS there is a way to check for empty strings '', by the way.

And I want to convert it to:

    enqueue(k: Truthy, obj?: any): void

except I don't know how to define the type for Truthy.

The reason I want this is that I don't want users to pass in null, undefined, '', etc, as the key to a hash.


Solution

  • I'm not sure why you need this but it's interesting. In all honesty the short answer is: TypeScript isn't geared for this and you'd probably be better off doing runtime checks and documenting your code so that developers are aware that the k param should be truthy. Still, if you're set on trying to force TypeScript to do something like this, read on:


    Note: for the below to work, turn on the strictNullChecks compiler option. It's kind of necessary, since being unable to distinguish Truthy from Truthy | null | undefined would be a problem.

    You can almost define falsy, which is like

    type Falsy = false | 0 | "" | null | undefined 
    

    except NaN is also falsy, and TypeScript doesn't have a numeric literal for NaN (see microsoft/TypeScript#15135).

    Even if you have Falsy as above, there are no negated types (see microsoft/TypeScript#4196) in TypeScript, so there's no way to express Truthy as "everything but Falsy".

    You could try to use use conditional types to exclude possibly-falsy parameters in enqueue(), but it is weird:

    type DefinitelyTruthy<T> =
      false extends T ? never :
      0 extends T ? never :
      "" extends T ? never :
      null extends T ? never :
      undefined extends T ? never :
      T
    
    declare function enqueue<T extends number | string | true | object>(
      k: T & DefinitelyTruthy<T>,
      obj?: any
    ): void
    
    declare const str: string;
    enqueue(str); // error, might be falsy
    enqueue("a"); // okay
    enqueue(1); // okay
    enqueue(0); // error
    enqueue(NaN); // error
    enqueue(true); // okay
    enqueue(false); // error
    enqueue([]); //okay
    enqueue({a: "hello"}); // okay
    enqueue({}); // error, interpreted as type {} which could be an empty string:
    const zilch = "" as {};
    enqueue(zilch); // error, see? 
    

    Note how it won't allow anything which it thinks might be falsy, which is possibly what you are trying to achieve. Can't tell.


    Update

    I see you edited the question to clarify that the k parameter should really be a string (or possibly a symbol) and that the only value you need to exclude is the empty string "". In that case you could simplify the above to:

    type DefinitelyNotEmptyString<T> = "" extends T ? never : T
    
    declare function enqueue<T extends string | symbol>(
      k: T & DefinitelyNotEmptyString<T>,
      obj?: any
    ): void
    
    enqueue(""); // error
    enqueue("a"); // okay
    

    All of that is great, but unfortunately there's the problem that if you pass a general string to enqueue() it will fail, and sometimes a developer might need to do that if the value they are using for the k parameter isn't a string literal they have specified:

    declare const str: string; // comes from somewhere else
    enqueue(str); // error!  how do I do this?
    

    To deal with this, you can try to create a nominal type which you can use to identify to the compiler that a value has been checked for emptiness, and then make a user-defined type guard to constrain a string to that type:

    type NotEmptyString = string & {"***NotEmptyString***": true};
    function notEmptyString(x: string): x is NotEmptyString {
      return x !== "";
    }
    

    Now the developer can do this:

    declare const str: string;
    enqueue(str); // error, might be falsy
    if (notEmptyString(str)) {
      enqueue(str); // okay, str is NotEmptyString
    }
    

    Whew! That's a lot of hoop jumping. It's up to you if you think this is worth it. Okay, hope that helps. Good luck!