Tslint: les expressions booléennes strictes ne doivent pas se déclencher pour les opérateurs logiques utilisés dans un contexte non booléen

Créé le 4 oct. 2017  ·  18Commentaires  ·  Source: palantir/tslint

Rapport d'erreur

  • __Version TSLint__ : 5.7
  • __TypeScript version__ : 2.5.3
  • __Exécution de TSLint via__ : CLI

Code TypeScript en train d'être lint

// code snippet
function valueOrDefault(a?: string) {
  return a || "the default";
}

avec la configuration tslint.json :

{
  "defaultSeverity": "error",
  "extends": [
      "tslint:all"
  ]
}

Comportement réel

Obtention du rapport de 2 erreurs :
strict-boolean-expressions This type is not allowed in the operand for the '||' operator because it could be undefined. Only booleans are allowed.
strict-boolean-expressions This type is not allowed in the operand for the '||' operator because it is always truthy. Only booleans are allowed.

Comportement prévisible

Aucune erreur signalée. J'utilise l'opérateur logique || pour fournir une valeur par défaut pour le paramètre (je sais que je pourrais utiliser une déclaration par défaut de paramètre de fonction, l'extrait est juste pour illustrer le problème général). Il n'y a aucun boolean en jeu nulle part : ni l'entrée ni la sortie de l'expression.

Fixed Bug

Commentaire le plus utile

À mon avis, strict-boolean-expressions ne devrait vérifier que l'opérande gauche de && et || . Ces opérateurs sont (essentiellement) du sucre pour les ternaires : a && b est égal à a ? b : a , et a || b est égal à a ? a : b . Quand vous y réfléchissez en ces termes, ignorer le RHS a beaucoup de sens, et cela alignerait le comportement des opérateurs de court-circuit sur celui des ternaires.

Ensuite , si le tout est dans un if/for/while, strict-boolean-conditions pourrait entrer en jeu et vérifier l'expression globale . Je pense que cela couvrirait tous les cas utiles, et je pourrais réactiver cette règle.

Le code qui a signalé cela comme un problème pour moi était ce modèle React commun documenté :

function Foo(props: { showToggle: boolean }) {
  return <div>{props.showToggle && <Toggle />}</div>;
}
ERROR: 2:36 strict-boolean-expressions This type is not allowed in the operand for the '&&' operator because it is always truthy. Allowed types are boolean, null-union, undefined-union, string, or number.

@ajafff Son code ne passe pas le linter pour moi, même avec toutes les options de la règle activées, à cause de la constante RHS :

export function valueOrDefault(a?: string) {
  return a || "the default";
}
"strict-boolean-expressions": [true, "allow-null-union", "allow-undefined-union", "allow-string", "allow-number", "allow-mix"]
ERROR: 2:15 strict-boolean-expressions This type is not allowed in the operand for the '||' operator because it is always truthy. Allowed types are boolean, null-union, undefined-union, string, or number.

L'intention de l'expression est conditionnelle à la LHS, donc ma proposition d'ignorer la RHS résoudrait également son problème.

Tous les 18 commentaires

Les pensées? Prières? Suggestions sur où/comment commencer ?

@marcind pouvez-vous fournir des exemples sur la façon dont il devrait se comporter différemment du retournement de la règle ?

Désolé, quelle partie de ma soumission initiale n'est pas claire ?

Je suppose que mon point est que cette règle ne devrait s'exécuter que dans un "contexte booléen", je peux penser à deux cas :

  1. Utilisé dans un if , while , ou for
  2. Affecté à une variable de type boolean .

La règle ne doit pas s'exécuter lorsque j'essaie de fournir une valeur par défaut ou une évaluation de court-circuit

  1. const a: string = potentiallyUndefinedString || "the default";
  2. const a: string | undefined = potentiallyUndefinedObject && potentiallyUndefinedObject.getString()

@marcind les exemples de votre dernier message fonctionneraient si vous utilisez l'option "allow-undefined-union" .

L'autre partie de votre demande doit être une règle distincte nommée strict-boolean-conditions qui vérifie uniquement if , for , while , do ... while et conditionnelle expressions ( x ? y : z ).
Je peux imaginer que cela ne vérifierait que le type de l'ensemble de la condition et non ses constituants :

function foo(a: boolean, b?: boolean) {
    if (b || a) {} // passes, result is always boolean
    if (b && a) {} // fails, result is boolean | undefined
    if (a || b) {} // fails, result is boolean | undefined
    if (a || !!b) {} // passes
}

Peut-être que cela peut être une option pour la règle existante au lieu d'une nouvelle règle...

À mon avis, strict-boolean-expressions ne devrait vérifier que l'opérande gauche de && et || . Ces opérateurs sont (essentiellement) du sucre pour les ternaires : a && b est égal à a ? b : a , et a || b est égal à a ? a : b . Quand vous y réfléchissez en ces termes, ignorer le RHS a beaucoup de sens, et cela alignerait le comportement des opérateurs de court-circuit sur celui des ternaires.

Ensuite , si le tout est dans un if/for/while, strict-boolean-conditions pourrait entrer en jeu et vérifier l'expression globale . Je pense que cela couvrirait tous les cas utiles, et je pourrais réactiver cette règle.

Le code qui a signalé cela comme un problème pour moi était ce modèle React commun documenté :

function Foo(props: { showToggle: boolean }) {
  return <div>{props.showToggle && <Toggle />}</div>;
}
ERROR: 2:36 strict-boolean-expressions This type is not allowed in the operand for the '&&' operator because it is always truthy. Allowed types are boolean, null-union, undefined-union, string, or number.

@ajafff Son code ne passe pas le linter pour moi, même avec toutes les options de la règle activées, à cause de la constante RHS :

export function valueOrDefault(a?: string) {
  return a || "the default";
}
"strict-boolean-expressions": [true, "allow-null-union", "allow-undefined-union", "allow-string", "allow-number", "allow-mix"]
ERROR: 2:15 strict-boolean-expressions This type is not allowed in the operand for the '||' operator because it is always truthy. Allowed types are boolean, null-union, undefined-union, string, or number.

L'intention de l'expression est conditionnelle à la LHS, donc ma proposition d'ignorer la RHS résoudrait également son problème.

Je suis d'accord que la vérification du RHS de && et || est un bug.


Je suis également enclin à relâcher les contrôles pour le LHS de && , || et l'opérande de ! . Ceux-ci ne doivent être vérifiés que si l'expression est toujours vraie ou toujours fausse.
Cela ne laisse que les conditions de if , for , while , do ... while et les expressions conditionnelles pour la vérification stricte pour autoriser uniquement les booléens (ou tout ce qui est configuré ). Pensées @adidahiya?

L'activation actuelle de cette règle avec les options de configuration disponibles nécessiterait des jours de travail avec notre base de code. :(

Je l'ai activé en espérant qu'il empêcherait la conversion implicite de number , null ou undefined en boolean . Par exemple:

if (!!array.length) { /* ... */ }


md5-d563d6246c0e981c16f8a2b3d7f53974





md5-430ec08bab82330fa4e519419e9ad014




However I find the following uses acceptable:



md5-70351fc6fdb328ee060919d6974e2cf4



```tsx


md5-036fd2ddd516eac8e1379cdcb9ac2b9a


Je pense que cela correspond à ce que suggère @marcind .

Je suis également enclin à assouplir les contrôles pour le LHS de &&, || et l' opérande de !.

@ajafff ​​est -ce que cela légaliserait if (!!array.length) ou if (!possiblyNull) ?

@ajafff @adidahiya d'autres réflexions ici ? Je suis exactement dans la même situation que @rhys-vdw.

Annoncer sans vergogne mon propre projet :
Depuis ma dernière activité dans ce numéro, j'ai créé mon propre linter, le projet Fimbullinter . Lisez la documentation pour un démarrage rapide : https://github.com/fimbullinter/wotan/blob/master/packages/wotan/README.md

Il contient une règle no-useless-predicate , qui pourrait être ce que vous recherchez. C'est une combinaison de strict-type-predicates et de strict-boolean-expressions de TSLint, mais avec quelques différences majeures :

Contrairement au strict-type-predicates de TSLint, il fonctionne également sans --strictNullChecks et gère correctement les paramètres de type et les types d'objets vides. (probablement pas très intéressant pour les abonnés à ce numéro)
La principale différence avec le strict-boolean-expressions de TSLint est qu'il n'exige pas que tout soit un booléen (il ne détecte pas la coercition implicite). Il faut juste que chaque condition soit peut-être vraie et fausse.

Quelques exemples:

if (0) {} // error, always falsy
if (1) {} // error, always truthy
declare let array: string[];
if (array.length) {} // no error
if (!array.length) {} // no error
if (!!array.length) {} // no error
if (array.length === undefined) {} // error, condition is always false
if (!!false) {} // 2 errors, because of the double negation of an always falsy value

declare let someString: string;
return someString || 'some default string'; // no error, because 'someString' might be falsy

declare const foo: 'bar' | 'baz';
return foo || 'bas'; // error, 'foo' is always truthy

declare let optionalFunction: (() => void) | undefined;
optionalFunction && optionalFunction(); // no error

J'aimerais aborder le cas d'utilisation suivant à partir de mon code actuel :

export interface ILoggingRule {
    readonly isFinal?: boolean;
    readonly loggerNamePattern?: string;
    readonly maxLogLevel?: LogLevel;
    readonly minLogLevel?: LogLevel;
    readonly target: Target;
}

// ...

/**
 * Creates an instance of LoggingRule.
 * <strong i="6">@param</strong> options Configuration options of the logging rule.
 */
public constructor(options: ILoggingRule) {
    // tslint:disable:strict-boolean-expressions
    this.isFinal = options.isFinal || false;
    this.loggerNamePattern = options.loggerNamePattern || "*";
    this.maxLogLevel = options.maxLogLevel || LogLevel.Fatal;
    this.minLogLevel = options.minLogLevel || LogLevel.Trace;
    this.target = options.target;
    // tslint:enable:strict-boolean-expressions
}

Comme vous pouvez le voir, j'utilise || pour désigner les valeurs par défaut si le LHS n'est pas défini. Existe-t-il un moyen concis de faire comprendre à tslint que cela respecte strict-boolean-expressions ? Sinon, je suis obligé d'utiliser ces commandes désactiver/activer. (Étonnamment, il ne s'agit que d'un problème avec la vérification de type activée et non dans VSCode.)

Cette utilisation est standard et extrêmement courante et pratique en JavaScript, et ne devrait certainement pas déclencher l'échec de cette règle.

À mon humble avis, le RHS de && et || ne doit pas être vérifié, pour permettre un court-circuit. Cela devrait être le comportement par défaut (ou au moins il devrait y avoir une option comme allow-any-rhs ). Actuellement, je ne peux pas utiliser strict-boolean-expressions , car il y a beaucoup de courts-circuits dans notre base de code.

La vérification de la LHS doit rester telle quelle à mon humble avis, car utiliser string ou number comme LHS peut être dangereux, voir cet exemple :

function foo(x: string | number | null) {
  return x || defaultValue;
}

C'est dangereux car de nombreux développeurs ne comprendront pas que non seulement null mais aussi 0 et "" provoqueront le retour de defaultValue .

J'ai dû faire face à des bogues majeurs dans mon projet, car les développeurs n'ont pas pris en compte que "" et 0 sont également faux. Empêcher ce type de code est mon principal cas d'utilisation pour strict-boolean-expressions .

Corrigé dans https://github.com/palantir/tslint/pull/4159 juste besoin d'un avis d'un collaborateur.

Oups, je voulais juste préciser que l'un de mes exemples était mauvais :

const x = possiblyUndefined || 5;

Cela ne devrait pas fonctionner car possiblyUndefined pourrait être 0 ce qui pourrait être une erreur logique. On dirait que les relations publiques de @dobesv fonctionneraient ici.

inclut pour la baie est une fonctionnalité ES7.
Vous devez modifier votre configuration pour utiliser ES7 ou ESNEXT

Cordialement,

@rimiti Bonjour, je n'arrive pas à comprendre à quoi vous faites référence, êtes-vous sûr d'avoir posté ce commentaire dans le bon fil ?

@dobesv Oh, tu as raison désolé ;)

corrigé par #4159, sera disponible dans la prochaine version

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

dashmug picture dashmug  ·  3Commentaires

allbto picture allbto  ·  3Commentaires

cateyes99 picture cateyes99  ·  3Commentaires

SwintDC picture SwintDC  ·  3Commentaires

denkomanceski picture denkomanceski  ·  3Commentaires