web-dev-qa-db-fra.com

Tapez pour non-falsy, aka truey

Dans TypeScript, existe-t-il un type pour véridique?

J'ai cette méthode: Object.keys (lck.lockholders) .length; enqueue (k: any, obj ?: any): void think with TS there is a way to check for empty strings '', soit dit en passant. et je veux le convertir en:

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

sauf que je ne sais pas comment définir le type de Truthy. I pensez avec TS qu'il y a un moyen de vérifier les chaînes vides '', soit dit en passant.

La raison pour laquelle je veux cela est parce que je ne veux pas que les utilisateurs passent null, undefined, '', etc., comme clé d'un hachage.

14
user7898461

Je ne sais pas pourquoi vous en avez besoin, mais c'est intéressant. En toute honnêteté, la réponse courte est: TypeScript n'est pas conçu pour cela et vous feriez probablement mieux de faire des vérifications d'exécution et de documenter votre code afin que les développeurs soient conscients que le paramètre k doit être véridique. Pourtant, si vous essayez de forcer TypeScript à faire quelque chose comme ça, lisez la suite:


Remarque: pour que ce qui suit fonctionne, activez l'option de compilateur strictNullChecks. C'est un peu nécessaire, car être incapable de distinguer Truthy de Truthy | null | undefined Serait un problème.

Vous pouvez presque définir falsy , ce qui est comme

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

sauf NaN est également faux, et TypeScript n'a pas un littéral numérique pour NaN.

Même si vous avez Falsy comme ci-dessus, il n'y a pas types niés dans TypeScript, donc il n'y a aucun moyen d'exprimer Truthy comme "tout sauf Falsy ".

Vous pouvez essayer d'utiliser use types conditionnels pour exclure les paramètres potentiellement faux dans enqueue(), mais c'est bizarre:

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? 

Notez comment il ne permettra rien de ce qu'il pense pourrait être faux, ce qui est probablement ce que vous essayez de réaliser. Je ne sais pas.


Mettre à jour

Je vois que vous avez modifié la question pour clarifier que le paramètre k devrait vraiment être un string (ou peut-être un symbol) et que la seule valeur que vous devez exclure est le vide chaîne "". Dans ce cas, vous pouvez simplifier ce qui précède pour:

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

Tout cela est génial, mais malheureusement, il y a le problème que si vous passez un string à enqueue() général, il échouera, et parfois un développeur pourrait avoir besoin de le faire si la valeur qu'il utilise pour le paramètre k n'est pas un littéral de chaîne qu'ils ont spécifié:

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

Pour y faire face, vous pouvez essayer de créer un type nominal que vous pouvez utiliser pour identifier au compilateur qu'une valeur a été vérifiée pour la vacuité, puis faire un type défini par l'utilisateur guard pour contraindre un string à ce type:

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

Maintenant, le développeur peut le faire:

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

Ouf! Cela fait beaucoup de sauts de cerceau. Cela dépend de vous si vous pensez que cela en vaut la peine. D'accord, j'espère que ça aide. Bonne chance!

15
jcalz

Il n'y a pas de type Truthy, [~ # ~] mais [~ # ~] vous pouvez utiliser le système de protection de type ( type prédicat ) pour aider TypeScript à comprendre ce qu'est vraiment truthy et à lui affecter truthy type!

Définissons le type Falsy et la fonction génériqueisTruthy:

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;

Maintenant, nous pouvons utiliser la fonction out isTruthy pour trouver les valeurs truthy et TypeScript attribuera correctement le "truthy" tapez le résultat.

Exemples:

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

1
icl7126