Typescript: Impossível definir a função de 'comprimento' estática na classe

Criado em 12 ago. 2014  ·  22Comentários  ·  Fonte: microsoft/TypeScript

O seguinte programa de digitação é aceito:

class C {
    static length ()  { return "twelve"; }
}

No entanto, no código gerado:

var C = (function () {
    function C() {
    }
    C.length = function () {
        return "twelve";
    };
    return C;
})();

Aqui, é feita uma tentativa de atribuir à propriedade length de uma função. Isso não funciona (pelo menos no Firefox e Chrome), fazendo com que as chamadas subsequentes de C.length() travem. Talvez a chamada de uma função de classe estática length deva apenas ser proibida?

Migrado do codeplex problema # 1260 .

Bug Fixed good first issue help wanted

Comentários muito úteis

Enquanto isso, o suporte nativo ES6 / 2015 chegou ao Chrome e ao Firefox. Se observarmos como eles lidam com o problema nativamente, chega perto do que @ nicolo-ribaudo e eu propomos (consulte # 9778).

Snippet para ilustrar o comportamento nativo:

class Foo {
    constructor() {}
}
class Bar {
    static length() {}
    static name() {}
    static caller() {}
}
Foo.name = "FooChanged";
Bar.name = "Baz";

console.log(Foo.name) // Logs "Foo". Foo.name remains unwritable
console.log(Bar.name) // Logs "Baz".  Bar.name became writable

Com relação ao que @tinganho escreveu anteriormente sobre a somente leitura de name , length e caller , podemos observar para as implementações nativas que as propriedades são graváveis ​​em uma função de construtor uma vez nós definimos qualquer um deles como um membro de classe estático (sinta-se à vontade para revisar ou modificar meus avisos recentes no MDN sobre suposições perigosas sobre Function.name para obter um nome de classe, por causa disso).

Portanto, usar Object.defineProperty como sugerido por @ nicolo-ribaudo iria emular o comportamento nativo com precisão. Algo que precisa ser considerado é que algumas versões de navegador mais antigas, mas compatíveis com ES5, definiram as propriedades como configurable: false . Portanto, tentar torná-los writable: true irá falhar para eles (por exemplo, veja a configurabilidade de Function.name )

Para resumir, minha opinião sobre o problema foi:

  • Não podemos usar Object.defineProperty() para o destino de compilação es3 . Sem Object.defineProperty , executar o código ES3 em um ambiente ES5, entretanto, teria o problema somente leitura. Portanto, precisamos de um erro de compilação para o destino es3
  • compile-o com Object.defineProperty() e writable: true para o destino de compilação es5 e emita um aviso / erro informando que o uso desses nomes de propriedade pode causar erros nos navegadores x of version <= y sugerindo que, se esses navegadores precisarem ser suportados, a opção mais segura seria escolher outro nome.
  • se você absolutamente não deseja que as pessoas usem as propriedades, gere um erro de compilação para qualquer um dos destinos.

Independentemente da saída da compilação, acho que qualquer tipo de mensagem adicional seria melhor do que a situação atual (TS 1.8.10), onde não temos nenhuma dica sobre possíveis problemas com a saída do compilador.

Editar : cortado adicionado

Todos 22 comentários

Corri para isso recentemente. :(

Além disso, alguns outros não são permitidos, por exemplo, o código a seguir deve apresentar erro (se fizermos esse recurso):

class C {
    static name = 'something';
    static arguments = 'args';
    static caller = 'caller';
}

console.log(C.name); // 'C'
console.log(C.arguments); // null
console.log(C.caller); // null

No entanto, nem todos são estelares.

Não acho que as propriedades name , caller e length são viáveis. Eles são propriedades somente leitura e não podem ser substituídos. Todas as atribuições a eles serão ignoradas.

@tinganho concordou

Ok, mas como as pessoas pegam esse tipo de erro de vez em quando, acho que o TS deveria notificá-las sobre fazer algo errado. É porque propriedades como name ou length são tão naturais de se ter em mente que as pessoas tentarão redefini-las repetidas vezes.

Pode ser porque Lengh, nome já está definido.

Vou tentar trabalhar nisso. Além do que já foi mencionado, existem mais nomes de propriedades que não deveriam ser permitidos?

Até agora eu vejo:
comprimento, nome, argumentos, chamador

O que exatamente deve acontecer quando alguém tenta definir uma propriedade estática com um dos nomes proibidos? Erro do compilador? Aviso visível para o usuário?

Eu sou novo nisso, então se eu estiver faltando alguma coisa, então alguma orientação seria apreciada. : +1:

Por que não compilar

class C {
    static name = 'something';
    static arguments = 'args';
    static caller = 'caller';
}

console.log(C.name); // 'C'
console.log(C.arguments); // null
console.log(C.caller); // null

para algo como

var C = (function () {
    function C() {
    }

    C.__statics = {
        name: 'something',
        arguments: 'args',
        caller: 'caller'
    };

    return C;
})();
console.log(C.__statics.name); // 'something'
console.log(C.__statics.arguments); // 'args'
console.log(C.__statics.caller); // 'caller'

Também surgiu uma pergunta sobre stackoverflow: http://stackoverflow.com/a/34644236/390330 : rose:

@zerkms, por favor, não poste o mesmo comentário em dois lugares; é confuso.

Às vezes, você não pode reescrever nomes arbitrariamente em um sistema de tipo estrutural.

Considere algum código como este

interface HasName {
  name: string;
}
class Foo {
  static name = 'fooClass';
}
let bar: HasName = { name: string };
let q = Math.random() > 0.5 ? Foo : bar;
console.log(q.name);

@RyanCavanaugh é um bom exemplo, de fato (eu não desenvolvo em TS, mas nossa - seu sistema de tipos é estranho assim que trata este código como válido).

Se não é possível transpilá-los sem risco, talvez devêssemos apenas proibi-los?

Se não é possível transpilá-los sem risco, talvez devêssemos apenas proibi-los?

Definitivamente, não alterando o transpile, apenas tornando-o um erro de compilação: rose:

[...] talvez devêssemos apenas desautorizá-los?

Algum desenvolvedor empreendedor deve nos enviar um PR! :piscadela:

Por que não usar Object.defineProperty ?

por exemplo

class C {
    static length() { return "twelve"; }
}

seria transplicado para algo como

var C = (function () {
    function C() {
    }
    Object.defineProperty(C, "length", {
        value: function () { return "twelve"; },
        writable: true
    });
    return C;
}());

@ nicolo-ribaudo Isso realmente funciona. Minha opinião: mas eu prefiro ver um erro em vez de tal transpile. O TypeScript geralmente se inclina para o erro elegante em vez de _fixar o JavaScript_: rose:

TypeScript geralmente se inclina para o lado do erro gracioso em vez de corrigir o JavaScript

A ideia do TS não é consertar o JS preenchendo as lacunas que ele não pode e nunca será capaz.

A ideia do TS não é consertar o JS preenchendo as lacunas que ele não pode e nunca será capaz.

sim. Mas entendendo como funciona o JavaScript. Tome um exemplo de null e undefined . O TypeScript opta por entender ambos (em vez de consolidá-lo em uma única coisa como o Dart faz https://www.dartlang.org/docs/synonyms/). O TypeScript fornece erros (em vez de corrigi-los no transpile) se você usar o padrão JavaScript errado: rose:

Minhas opiniões são minhas e não são endossadas por ninguém além de mim: rose:

Enquanto isso, o suporte nativo ES6 / 2015 chegou ao Chrome e ao Firefox. Se observarmos como eles lidam com o problema nativamente, chega perto do que @ nicolo-ribaudo e eu propomos (consulte # 9778).

Snippet para ilustrar o comportamento nativo:

class Foo {
    constructor() {}
}
class Bar {
    static length() {}
    static name() {}
    static caller() {}
}
Foo.name = "FooChanged";
Bar.name = "Baz";

console.log(Foo.name) // Logs "Foo". Foo.name remains unwritable
console.log(Bar.name) // Logs "Baz".  Bar.name became writable

Com relação ao que @tinganho escreveu anteriormente sobre a somente leitura de name , length e caller , podemos observar para as implementações nativas que as propriedades são graváveis ​​em uma função de construtor uma vez nós definimos qualquer um deles como um membro de classe estático (sinta-se à vontade para revisar ou modificar meus avisos recentes no MDN sobre suposições perigosas sobre Function.name para obter um nome de classe, por causa disso).

Portanto, usar Object.defineProperty como sugerido por @ nicolo-ribaudo iria emular o comportamento nativo com precisão. Algo que precisa ser considerado é que algumas versões de navegador mais antigas, mas compatíveis com ES5, definiram as propriedades como configurable: false . Portanto, tentar torná-los writable: true irá falhar para eles (por exemplo, veja a configurabilidade de Function.name )

Para resumir, minha opinião sobre o problema foi:

  • Não podemos usar Object.defineProperty() para o destino de compilação es3 . Sem Object.defineProperty , executar o código ES3 em um ambiente ES5, entretanto, teria o problema somente leitura. Portanto, precisamos de um erro de compilação para o destino es3
  • compile-o com Object.defineProperty() e writable: true para o destino de compilação es5 e emita um aviso / erro informando que o uso desses nomes de propriedade pode causar erros nos navegadores x of version <= y sugerindo que, se esses navegadores precisarem ser suportados, a opção mais segura seria escolher outro nome.
  • se você absolutamente não deseja que as pessoas usem as propriedades, gere um erro de compilação para qualquer um dos destinos.

Independentemente da saída da compilação, acho que qualquer tipo de mensagem adicional seria melhor do que a situação atual (TS 1.8.10), onde não temos nenhuma dica sobre possíveis problemas com a saída do compilador.

Editar : cortado adicionado

@RyanCavanaugh : você poderia revisitar este problema para o marco TS 2.0.1. A solução parece simples, mas dependendo de qual opção você escolher, a saída do emissor pode precisar mudar (veja meu comentário anterior). O lançamento do TS 2.0, então, pode ser melhor incluir a correção.

As tags no PR indicam que a equipe do TypeScript, que tem recursos limitados, está deixando isso em aberto para que a comunidade resolva. Se você quiser que esse problema seja resolvido, alguém na comunidade mais ampla terá que lidar com isso até que a equipe principal do TypeScript sinta que tem espaço suficiente no backlog para colocá-lo em um lançamento (o que provavelmente seria um longo tempo).

@kitsonk Obrigado por explicar o rótulo de relações públicas. Acho que entendi isso. O que sugeri foi reavaliar se ainda é a maneira certa de lidar com o problema. Propriedades estáticas de classe chamadas _nome_ ou _comprimento_ não são tão improváveis ​​de acontecer e o TS produz uma saída errônea para isso. Portanto, eu nem chamaria o problema de "caso extremo".

Considero as propriedades estáticas no centro da linguagem e, mesmo que ainda não haja solicitações de pull, soluções para o problema foram apresentadas neste tópico.

No entanto, irei avaliar se posso fornecer um PR, mas devo admitir que ainda não tenho muito conhecimento sobre as fontes do TS e temo que o TS 2.0 seja lançado antes.

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

Questões relacionadas

OliverJAsh picture OliverJAsh  ·  242Comentários

jonathandturner picture jonathandturner  ·  147Comentários

quantuminformation picture quantuminformation  ·  273Comentários

tenry92 picture tenry92  ·  146Comentários

RyanCavanaugh picture RyanCavanaugh  ·  205Comentários