Tslint: Expressões booleanas estritas não devem ser disparadas para operadores lógicos usados ​​em um contexto não booleano

Criado em 4 out. 2017  ·  18Comentários  ·  Fonte: palantir/tslint

Relatório de erro

  • __TSLint version__: 5.7
  • __TypeScript version__: 2.5.3
  • __ Executando TSLint via__: CLI

Código TypeScript sendo vinculado

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

com configuração tslint.json :

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

Comportamento real

Obtendo relatório de 2 erros:
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.

Comportamento esperado

Nenhum erro sendo relatado. Estou usando o operador lógico || para fornecer um valor padrão para o parâmetro (eu sei que poderia usar uma declaração padrão de parâmetro de função, o snippet é apenas para ilustrar o problema geral). Não há boolean em jogo em lugar nenhum: nem a entrada nem a saída da expressão.

Fixed Bug

Comentários muito úteis

Na minha opinião, strict-boolean-expressions deve verificar apenas o operando esquerdo de && e || . Esses operadores são (essencialmente) açúcar para ternários: a && b é igual a a ? b : a e a || b é igual a a ? a : b . Quando você pensa sobre isso nesses termos, ignorar o RHS faz muito sentido e colocaria o comportamento dos operadores em curto-circuito em linha com o comportamento dos ternários.

Então , se tudo estiver dentro de um if / for / while, strict-boolean-conditions pode entrar em ação e verificar a expressão geral . Acho que isso cobriria todos os casos úteis e eu seria capaz de ativar essa regra novamente.

O código que sinalizou isso como um problema para mim foi este padrão React comum documentado:

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 Seu código não passa o linter para mim, mesmo com todas as opções da regra habilitadas, devido ao RHS constante:

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.

A intenção da expressão é uma condição do LHS, portanto, minha proposta de ignorar o RHS também resolveria o problema dele.

Todos 18 comentários

Pensamentos? Orações? Sugestões sobre onde / como começar?

@marcind, você pode fornecer exemplos de como ele deve se comportar de maneira diferente de virar a regra?

Desculpe, qual parte do meu envio inicial não está claro?

Acho que meu ponto é que essa regra só deve ser executada em um "contexto booleano", posso pensar em dois casos:

  1. Usado em um if , while ou for
  2. Atribuído a uma variável digitada como boolean .

A regra não deve ser executada quando estou tentando fornecer um valor padrão ou avaliação de curto-circuito

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

@marcind os exemplos de sua última postagem funcionariam se você usar a opção "allow-undefined-union" .

A outra parte da sua solicitação deve ser uma regra separada chamada strict-boolean-conditions que verifica apenas if , for , while , do ... while e condicional expressões ( x ? y : z ).
Posso imaginar que apenas verificaria o tipo de toda a condição e não seus constituintes:

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
}

Talvez isso possa ser uma opção para a regra existente em vez de uma nova regra ...

Na minha opinião, strict-boolean-expressions deve verificar apenas o operando esquerdo de && e || . Esses operadores são (essencialmente) açúcar para ternários: a && b é igual a a ? b : a e a || b é igual a a ? a : b . Quando você pensa sobre isso nesses termos, ignorar o RHS faz muito sentido e colocaria o comportamento dos operadores em curto-circuito em linha com o comportamento dos ternários.

Então , se tudo estiver dentro de um if / for / while, strict-boolean-conditions pode entrar em ação e verificar a expressão geral . Acho que isso cobriria todos os casos úteis e eu seria capaz de ativar essa regra novamente.

O código que sinalizou isso como um problema para mim foi este padrão React comum documentado:

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 Seu código não passa o linter para mim, mesmo com todas as opções da regra habilitadas, devido ao RHS constante:

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.

A intenção da expressão é uma condição do LHS, portanto, minha proposta de ignorar o RHS também resolveria o problema dele.

Eu concordo que verificar o RHS de && e || é um bug.


Também estou inclinado a relaxar as verificações para o LHS de && , || e o operando de ! . Estes devem ser verificados apenas se a expressão é sempre verdadeira ou sempre falsa.
Isso deixa apenas as condições de if , for , while , do ... while e expressões condicionais para a verificação estrita para permitir apenas booleanos (ou qualquer outra coisa que esteja configurada ) Pensamentos @adidahiya?

Atualmente, a habilitação dessa regra com as opções de configuração disponíveis exigiria dias de trabalho com nossa base de código. :(

Eu o habilitei esperando evitar a conversão implícita de number , null ou undefined em boolean . Por exemplo:

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


md5-d563d6246c0e981c16f8a2b3d7f53974





md5-430ec08bab82330fa4e519419e9ad014




However I find the following uses acceptable:



md5-70351fc6fdb328ee060919d6974e2cf4



```tsx


md5-036fd2ddd516eac8e1379cdcb9ac2b9a


Acho que isso está de acordo com o que sugere @marcind .

Também estou inclinado a relaxar as verificações do LHS de &&, || e o operando de!.

@ajafff isso legalizaria if (!!array.length) ou if (!possiblyNull) ?

@ajafff @adidahiya mais alguma opinião aqui? Estou exatamente na mesma situação que @ rhys-vdw.

Anunciando descaradamente meu próprio projeto:
Desde minha última atividade nesta edição criei meu próprio linter, o projeto Fimbullinter . Leia a documentação para um início rápido: https://github.com/fimbullinter/wotan/blob/master/packages/wotan/README.md

Ele contém uma regra no-useless-predicate , que pode ser o que você está procurando. É uma combinação de strict-type-predicates e strict-boolean-expressions do TSLint, mas com algumas diferenças importantes:

Em contraste com o strict-type-predicates do TSLint também funciona sem --strictNullChecks e trata corretamente os parâmetros de tipo e os tipos de objetos vazios. (provavelmente não é tão interessante para as pessoas que se inscreveram nesta edição)
A principal diferença para o strict-boolean-expressions do TSLint é que ele não exige que tudo seja booleano (ele não detecta coerção implícita). Exige apenas que todas as condições sejam possivelmente verdadeiras e falsas.

Alguns exemplos:

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

Eu gostaria de comentar o seguinte caso de uso do meu código real:

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
}

Como você pode ver, estou usando || para denotar valores padrão se o LHS for indefinido. Existe alguma maneira concisa de fazer tslint entender que está de acordo com strict-boolean-expressions ? Caso contrário, sou forçado a usar os comandos de ativação / desativação. (Surpreendentemente, este é apenas um problema com a verificação de tipo ativada e não no VSCode.)

Esse uso é padrão e extremamente comum e conveniente em JavaScript e, definitivamente, não deve fazer com que essa regra falhe.

IMHO, o RHS de && e || não deve ser verificado, para permitir curto-circuito. Este deve ser o comportamento padrão (ou pelo menos deve haver uma opção como allow-any-rhs ). Atualmente, não posso usar strict-boolean-expressions , pois há muitos curto-circuitos em nosso código-base.

Verificar o LHS deve permanecer como está IMHO, uma vez que usar string ou number como LHS pode ser perigoso, veja este exemplo:

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

Isso é perigoso, pois muitos desenvolvedores não entenderão que não apenas null mas também 0 e "" farão com que defaultValue seja devolvido.

Tive que lidar com os principais bugs em meu projeto, devido aos desenvolvedores não levarem em consideração que "" e 0 também são falsey. Prevenir esse tipo de código é meu principal caso de uso para strict-boolean-expressions .

Corrigido em https://github.com/palantir/tslint/pull/4159 só precisa da revisão de um colaborador.

Ops, só queria esclarecer que um dos meus exemplos era ruim:

const x = possiblyUndefined || 5;

Isso não deveria funcionar porque possiblyUndefined pode ser 0 que pode ser um erro lógico. Parece que o RP de @dobesv funcionaria aqui.

inclui para array é um recurso ES7.
Você precisa alterar sua configuração para usar ES7 ou ESNEXT 🦄

Atenciosamente, 🚀

@rimiti Olá, não consigo entender a que você está se referindo. Tem certeza de que postou esse comentário no tópico certo?

@dobesv Oh, sinto muito;)

corrigido por # 4159, estará disponível na próxima versão

Esta página foi útil?
0 / 5 - 0 avaliações