Type for non-falsy, aka truthy

99封情书 提交于 2020-12-26 05:18:45

问题


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 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. I think with TS there is a way to check for empty strings '', by the way.

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


回答1:


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!




回答2:


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



来源:https://stackoverflow.com/questions/51390276/type-for-non-falsy-aka-truthy

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!