Type for non-falsy, aka truthy

后端 未结 2 2024
余生分开走
余生分开走 2021-01-17 08:33

In TypeScript, is there a type for truthy?

I have this method: Object.keys(lck.lockholders).length; enqueue(k: any, obj?: any): void think with TS there is a

相关标签:
2条回答
  • 2021-01-17 08:34

    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.

    Even if you have Falsy as above, there are no negated types 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!

    0 讨论(0)
  • 2021-01-17 08:56

    There is no Truthy type, BUT you can leverage type guard system (type predicate) to help TypeScript to understand what really is truthy and assign it truthy type!

    Let's define Falsy type and generic isTruthy function:

    type Falsy = false | 0 | '' | null | undefined;
    
    // this is a type predicate - if x is `truthy`, then it's T
    const isTruthy = <T>(x: T | Falsy): x is T => !!x;
    
    

    Now we can use out isTruthy function to find truthy values and TypeScript will correctly assign the "truthy" type to the result.

    Examples:

    {   // removing boolean "false" type
      const result: string | false = Math.random() > 0.5 ? 'a' : false;
      const truthyResult: string = isTruthy(result) ? result : 'was false';
    }
    {   // filtering only numbers from array
      const result: (number | undefined)[] = [42, undefined, 8, 16];
      const truthyResult: number[] = result.filter(isTruthy);
    }
    {   // filtering only Promises from array
      const result: (Promise<string> | null)[] = [Promise.resolve('a'), null, Promise.resolve('b'), Promise.resolve('c')];
      const truthyResult: Promise<string>[] = result.filter(isTruthy);
    }
    
    
    0 讨论(0)
提交回复
热议问题