Tslint: Conflito de regras 'no-inferrable-types' e 'typedef'

Criado em 3 out. 2015  ·  33Comentários  ·  Fonte: palantir/tslint

Oi de novo,

Não tenho certeza se isso é um problema ou uma decisão de design, mas posso ver conflito entre as regras no-inferrebale-types e typedef .

Ex.

function fn(): void {
    for (let i = 0; i < 100; i++) {
        console.log(i);
    }
}

Trecho de https://github.com/palantir/tslint/blob/master/docs/sample.tslint.json :

 "typedef": [true, ...],
 "no-inferrable-types": true,

Quando eu tenho essas duas regras ativadas, recebo:

error (typedef) test.ts[2, 12]: expected variable-declaration: 'i' to have a typedef

Adicionar anotação de tipo number à variável i causa o seguinte erro:

error (no-inferrable-types) test.ts[2, 14]: LHS type (number) inferred by RHS expression, remove type annotation

Existe alguma maneira de ter essas duas regras coexistindo entre si?

Por exemplo, eu quero ter variáveis ​​inferíveis declaradas diretamente com tipo primitivo (como a regra doc diz: number , boolean ou string ), mas por outro lado, eu quero para forçar typedefs em tipos não primitivos.

Obrigado,

O.

P1 Enhancement 🌹 R.I.P. 🌹

Comentários muito úteis

:+1: Idealmente (imo) eu gostaria de uma maneira de dizer "sempre exigir um typedef, a menos que o tipo seja inferível". O que eu acho que não é possível agora.

Todos 33 comentários

Boa captura, obrigado pelo alerta. Eu adicionei a regra no-inferrable-types sem pensar em como ela pode entrar em conflito com a regra typedef , opa! Por enquanto eu recomendo desligar um dos dois, ou usar apenas algumas opções da regra typedef .

A longo prazo, acho que queremos integrar no-inferrable-types na regra typedef ou queremos pelo menos que o TSLint detecte configurações conflitantes, como ter ambas as regras ativadas.

Eu estou enfrentando o mesmo problema.
Desativei os tipos não-inferíveis por enquanto.

Sim mesmo aqui, eu gostaria de ter a capacidade de ter as duas regras coexistindo.
Eu não gostaria que a regra typedef fosse ativada se o tipo da variável pudesse ser inferido.
ou seja, "tipos não inferíveis" devem ter prioridade sobre "typedef"

+1

let id: number = 0;
for (let job: string of NAMES_PROFFESSIONS) {
     /** some code */
     id++;
}

(tipos não inferíveis) Tipo LHS (número) inferido pela expressão RHS, remova a anotação do tipo

+1

Sua definição de tipos "inferíveis" inclui atribuições de construtor?

// BAD (this hurts my eyes to read)
let labels: Map<string, string> = new Map<string, string>();
// GOOD (type is obvious)
let labels = new Map<string, string>();

mas também...

// BAD (in a diff, it's not obvious what this type is)
let labels = this.buildLabels();
// GOOD
let labels: Map<string, string> = this.buildLabels();

Sim, é perigoso. Se eu quiser simplificar meu código e impedir o uso de declaração de tipo para variáveis ​​​​iniciadas diretamente, não posso fazer isso estritamente e isso leva a tal coisa:

let x = 2;
let y;
let z: number;

x = 1;
y = 1;
z = 1;
x = 's'; // Type 'string' is not assignable to type 'number'
y = 's'; // It's OK
z = 's'; // Type 'string' is not assignable to type 'number'

Pode ser uma opção muito útil permitir a declaração de tipo de salto apenas para variáveis ​​inicializadas.

… e realmente não apenas para tipos primitivos, como diz @pgonzal !
Olha isso, é terrível:

const onChange: () => void = () => this.update();

:+1: Idealmente (imo) eu gostaria de uma maneira de dizer "sempre exigir um typedef, a menos que o tipo seja inferível". O que eu acho que não é possível agora.

Eu me deparei com isso e fiz um sinalizador ignore-params que ajuda no meu caso. Eu quero forçar typedefs para todos os parâmetros de método/função, mesmo quando eles podem ser facilmente inferidos.

Veja PR: #1190 se você quiser experimentar

Já faz um tempo desde que a edição original foi enviada. A opção recomendada neste momento é desabilitar no-inferrable-types e incluir o tipo em tudo? Qualquer outra coisa para tentar habilitar e combinar no-inferrable-types e typedef parece um hack e resulta em um monte de avisos inúteis. Esperando uma solução melhor em um futuro próximo.

@corydeppen Observe os 8 polegares para cima na sugestão de @englercj , acima. Não está claro o que "combinar" significa em seu comentário "parece um hack". Se você quer dizer "usar juntos em tslint.json , então sim. Mas "combinar" um argumento optional-inferrable-types em typedef seria ótimo (pelo menos, para 9 de nós).

Podemos obter uma atualização sobre isso, por favor?

Eu acho que a melhor maneira de lidar com isso é depreciar o no-inferrable-types e apenas passar um objeto de opção para typedef para ignorar a falta de definições de tipo se o tipo for inferível de acordo com certos padrões, como initialized , initialized primitives , call signatures e outros padrões que atenderiam nossas necessidades como desenvolvedores.

Para mim, isso faz mais sentido, porque sempre deve haver um typedef, a menos que haja algo dizendo qual é o tipo da função. E seria configurável também, porque talvez queiramos que o properties inicializado em uma classe tenha tipo inferível, mas não para o call signatures por exemplo.

Seria ótimo se alguém pudesse contribuir para resolver isso. Até então, somos forçados a escolher entre "declarações de tipo insuficientes para serem legíveis" versus "desordenado com muitas declarações de tipo".

Esse problema está aberto desde 3 de outubro de 2015 - desde então, minha equipe criou cerca de 2.000 arquivos de origem TypeScript que estão todos desordenados com muitas declarações de tipo.

O typedef aceita uma configuração. Então, talvez apenas outra configuração apenas para ignorar a definição de tipo se o tipo for inferível, o que significa

Basta digitar onde não está sendo inicializado.

Parece que porque não há um consenso claro sobre como lidar com a questão, nenhum progresso está sendo feito. Exemplo de comentário: https://github.com/theia-ide/theia/issues/356#issuecomment -319350833

Suspeito que todos concordamos que qualquer solução é melhor do que deixar as coisas como estão. Se você acha que qualquer mudança no status quo em relação a essa questão é boa, por favor, 👍 este comentário. Se você tem conhecimento suficiente para criar um PR para corrigir esse problema, ajude a todos nós ❤️ .

Aceitaria um PR para:

  • adicione uma opção a typedef que permite ignorar casos em que no-inferrable-types diz para não fornecer um tipo

O typedef aceita uma configuração. Então, talvez apenas outra configuração apenas para ignorar a definição de tipo se o tipo for inferível, significando 'Apenas digite onde não estiver sendo inicializado.'

A regra typedef é para pessoas que gostam de ter definições de tipo explícitas em sua base de código. Se você deseja que o comportamento acima "apenas digite onde não está sendo inicializado", é melhor desabilitar typedef e certificar-se de ter noImplictAny habilitado como uma opção do compilador TypeScript.

Há também o caso complicado em que algumas coisas que são inicializadas precisam de um typedef de qualquer maneira, como no trecho a seguir:

interface Literal {
    field: "value"
}

const literal0 = {
    field: "value",
};
const literal1: Literal = {
    field: "value",
};

const func = (obj: Literal) => { };
func(literal0);  // Error! Type 'string' is not assignable to type '"value"'.
func(literal1);

Além disso, enquanto corrigimos isso, gostaríamos de mencionar que provavelmente existem algumas ótimas regras de terceiros por aí, como no-unnecessary-type-annotation (https://github.com/ajafff/tslint-consistent-codestyle/blob/ master/docs/no-unnecessary-type-annotation.md) por exemplo. Se alguém souber de outras regras de terceiros que forneçam o comportamento desejado, poste-as aqui e podemos recomendá-las oficialmente ou adotá-las no núcleo, se fizer sentido.

@JKillian obrigado pela recomendação, acho que era isso que eu queria. Há um post muito bom sobre como evitar o tipo any : Não use "naked any", crie uma "qualquer interface".

Cerca de:

interface Literal {
    field: "value"
}

const literal0 = {
    field: "value",
};
const literal1: Literal = {
    field: "value",
};

const func = (obj: Literal) => { };
func(literal0);  // Error! Type 'string' is not assignable to type '"value"'.
func(literal1);

Não vejo como isso poderia ser um comportamento indesejado nem um caso complicado. Você quer ter certeza de que obj tem uma propriedade field com valor value , e mesmo quando você está inicializando literal0 com uma propriedade que parece com as restrições , você pode modificar isso para outra string.

Eu sei que não é um bom caso de uso, mas na maioria dos casos quando você está usando um literal, você provavelmente quer esse literal, não um primitivo.

Tenho a seguinte configuração:
json "no-inferrable-types": true, "typedef": [true, "call-signature", "parameter"],
E este código:
javascript private static readonly DEVICE_UID: string = 'device_uid'; private static readonly DEVICE_PLATFORM: string = 'browser'; private static readonly AGENT_DEFAULT_ICON = 'http://localhost:3000/icon.png';

Por que não estou recebendo erro nas duas primeiras declarações?

@sandrocsimas Interessante, mas fora do tópico eu acho; AFAICT esse problema não está relacionado a este problema. Eu sugiro que você comece outro problema (fwiw!).

@estaub , sim, eu vou. Estou obtendo o mesmo comportamento, mesmo sem a regra typedef.

@sandrocsimas isso ocorre porque é uma propriedade somente leitura e esse Typescript infere seu tipo como literal. Digitando-o como uma string você está dizendo que deve ter uma string, não necessariamente terá esse valor literal e o valor não deve mudar estaticamente.

Seria bom ter uma regra 'require-typedef-except-inferrable'.

@FiretronP75 como @JKillian disse, essa é apenas a opção noImplicitAny do TSC.

@michaeljota obrigado, não percebi que a opção noImplicitAny do compilador dá exceções para inferíveis. Ainda seria bom ter no tslint, para a opção de torná-lo um aviso em vez de interromper a compilação, e por ter os sinalizadores de comentário tslint.

Eu vejo por que isso seria algo desejado, mas tendo no-unused-variables como exemplo, não acho que os casos de uso cobertos pelo TSC serão suportados pela equipe do TSLint. Eu sei que não é o mesmo _linter error_ que um _compiler error_ mas no final, ambos são sobre um código melhor escrito. Hoje em dia com soluções como Webpack ou Parcel que permitem compilar e executar o código mesmo com erros de TSC, não vejo isso como um problema real.

Isso foi corrigido na versão mais recente?

Eu ainda não acho que isso está no roteiro. Você deve considerar usar noImplicitAny do TSC

☠️ Chegou a hora do TSLint! ☠️

O TSLint não está mais aceitando a maioria das solicitações de recursos por #4534. Veja typescript-eslint.io para a nova e brilhante maneira de lint seu código TypeScript com ESLint . ✨

Foi um prazer abrir o código com todos vocês!

🤖 Bip boop! 👉 TSLint está obsoleto 👈 _(#4534)_ e você deve mudar para typescript-eslint ! 🤖

🔒 Esta questão está sendo bloqueada para evitar mais discussões desnecessárias. Obrigada! 👋

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