Typescript: Sugestão: "operador de navegação segura", ou seja, x?.y

Criado em 15 jul. 2014  ·  205Comentários  ·  Fonte: microsoft/TypeScript

Status atual

  • A proposta TC39 está agora na fase 3 (🎉🎉🎉🎉🎉)
  • A implementação está em andamento
  • Você pode esperar esse recurso no TypeScript 3.7
  • Atualizaremos aqui quando estiver disponível em uma versão noturna
  • Suspensão da Chamada Opcional até que sua semântica seja esclarecida no comitê

Perguntas abertas

  • Que invólucro especial, se houver, deve document.all obter?

C# e outras linguagens têm açúcar de sintaxe para acessar cadeias de propriedades onde null (ou em nosso caso, undefined ) pode ser encontrado em qualquer ponto da hierarquia de objetos.

var x = { y: { z: null, q: undefined } };
console.log(x?.y?.z?.foo); // Should print 'null'
console.log(x?.baz); // Still an error
console.log(x.y.q?.bar); // Should print 'undefined'

Precisa de proposta sobre o que exatamente devemos codificar, tendo em mente os efeitos colaterais dos acessadores.


Editado por @DanielRosenwasser 27 de fevereiro de 2018: Esta proposta também é chamada de operador "propagação nula".

Committed ES Next Suggestion Update Docs on Next Release

Comentários muito úteis

O encadeamento opcional é o Estágio 3

Desbloqueando brevemente este apenas para fins comemorativos

Todos 205 comentários

Então, no primeiro exemplo, podemos emitir da seguinte forma:

x && xy && xyz && xyzfoo

Mas então teríamos que de alguma forma fazer com que x, y, z e foo fossem avaliados no máximo uma vez.

Você também não pode fazer && em muitos casos porque a veracidade se torna um problema para os primitivos.

Por exemplo:

"     "?.trim()?.indexOf("hello")

"" .

Portanto, você precisa fazer algumas comparações explícitas com null usando == para o caso geral, a menos que aproveitemos o sistema de tipos (o que seria muito legal nos ver fazer).

Poderíamos emitir uma função monadic-bind (não muito bonita para a saída JS) ou usar alguma transformação em operadores ternários (mais próximo do equivalente típico de JS). Eu sou claramente um pouco tendencioso para o último.

:+1:

Idealmente, teríamos ES7 (ou ES8 ou ES9 ou ...) implementando isso primeiro, pois provavelmente haveria algum desacordo sobre a semântica exata sobre usar ou não 0 / "" como primitivos falsos para os propósitos de qualquer operador aqui.

:+1: Eu gostaria de ver o TypeScript receber isso primeiro sem ter que esperar pelo ESxx.

O fato de que operadores de segurança nula simples e insanamente úteis como "?." e "?:" NÃO ESTÃO na especificação do ES6 significa que as pessoas que montam a especificação do ES6 deveriam estar com a cabeça baixa de vergonha. Isso é uma coisa tão simples e óbvia que não incorporá-la seria francamente insano. Há uma razão pela qual a maioria das linguagens modernas suporta isso: elas são indispensáveis.

Eu percebo que isso seria um desvio da especificação atual (já que a especificação atual é tão míope a ponto de omitir isso). Mas é tão ridiculamente útil que acho que esse único desvio seria justificado. A grande maioria (VAST) dos desenvolvedores de TS não seria afetada por pequenas alterações na implementação, se ou quando isso finalmente for adicionado a uma especificação de ES. Os enormes benefícios que isso ofereceria valem o potencial impacto futuro para uma pequena fração de desenvolvedores. E dado o processo de especificação ES ridiculamente lento, isso nem importaria por vários anos (no mínimo).

concordo totalmente com o brain428

@ brian428 o problema aqui é que esse operador pode ser implementado no ES7, portanto, se o texto datilografado for com uma especificação que acabe sendo diferente da final do ES7, ninguém ficará feliz.

o problema aqui é que esse operador pode ser implementado no ES7, então se o typescript for com uma especificação que acaba sendo diferente da final do ES7, ninguém ficará feliz.

Acho que é uma abordagem mais positiva para o TypeScript implementar recursos que podem _potencialmente _ (ou não) chegar a uma versão futura do ES, porque será um teste útil para influenciar a direção do ES.

Aqui está um exemplo de discussão ES sendo influenciada pelo TypeScript :

A opção TypeScript... para declarar e inicializar por meio de um prefixo privado em um dos parâmetros do construtor seria útil para muitos desenvolvedores

Além disso, certamente é possível que o ES adote um recurso que já está presente no TypeScript, mas com semântica diferente (por exemplo, sobre como os módulos funcionam).

certamente é possível que o ES adote um recurso que já está presente no TypeScript, mas com semântica diferente

Devo observar que consideramos amplamente este o pior cenário possível. Nós realmente queríamos que os módulos no ES6 fossem finalizados antes de declararmos o TypeScript 1.0, mas os atrasos no cronograma do comitê impediram isso. Isso é algo a ser evitado, não repetido. Gostaríamos muito de encontrar recursos que tenham ~0% de chance de chegar ao ES7+ (por exemplo, anotações de tipo), ou ~100% de chance de chegar com semântica facilmente definida (por exemplo, onde a seta gorda era dois anos atrás). Novos operadores provavelmente cairão no meio estranho.

Na pior das hipóteses, se o ES7 for diferente, um sinalizador do compilador poderia suportar a implementação do TS legado, oferecendo assim um período de carência? Isso, juntamente com uma documentação de migração clara, deve oferecer aos desenvolvedores uma rota direta para qualquer novo padrão.

Em última análise, o uso de qualquer recurso – embora incrivelmente útil – não é essencial para os desenvolvedores. O TS deve deixar as possíveis implicações futuras de seu uso bem claras desde o primeiro dia. Não goste da ideia de uma refatoração gerenciada em potencial, não a use. Talvez um sinalizador de compilador opt-in para impor esta mensagem?

O TS não deveria enlouquecer querendo influenciar o ES, mas em pequenos casos isolados como esse, seria uma pena se o TS se esquivasse completamente.

Talvez pudéssemos montar uma proposta de espantalho para isso e então ter uma implementação de referência por trás de um sinalizador --harmony (ou algo assim). Dessa forma, podemos impulsionar o desenvolvimento ES7 desse recurso.

Para evitar efeitos colaterais devido a pesquisas repetidas, o compilador terá que gerar variáveis ​​temporárias:

($tmp0 = x, $tmp0 === void 0 ? void 0 : 
    ($tmp1=$tmp0.y,  $tmp1 === void 0 ? void 0 : 
        ($tmp2 = $tmp1.z,  $tmp2 === void 0 ? void 0 : $tmp2)))

ou use uma membrana de memorização baseada em Proxy .

De um ponto de vista categórico, essa é apenas a mônada talvez aplicada à pesquisa de propriedades, portanto, é um recurso muito natural para uma linguagem em que todas as pesquisas de propriedades podem retornar indefinidas. Eu ficaria surpreso se o ES7 adotasse qualquer semântica diferente da descrita pelo código acima.

A questão do codeplex teve um grande número de votos (61)

Eu realmente preciso disso para aliviar a dor de usar atom para atom-typescript .

É muito idiomático em código coffescript (embora eu gostaria que não fosse tão popular quanto o determinismo é melhor do que um ? fudgy). Abra qualquer arquivo coffescript, especialmente um que funcione com o DOM diretamente como space-pen (onde as funções podem ser executadas após a exibição ser destruída ou antes da exibição ser anexada) e você encontrará um zilhão de ? usos. por exemplo, este arquivo tem 16 https://github.com/atom-community/autocomplete-plus/blob/f17659ad4fecbd69855dfaf00c11856572ad26e7/lib/suggestion-list-element.coffee

Novamente, eu não gosto que eu precise disso, mas é o estado do JavaScript, e eu prefiro ? do que um milhão de if( && fest ) { then }

Mas eu realmente preciso dele para manter meu código legível. Também é muito comum _precisar_ disso quando você está esperando que um XHR seja concluído e o angular execute seu loop de resumo.

Ok, agora eu li o tópico e vejo por que estamos esperando. Eu entendo _suspiro_.

we'd have to somehow make x, y, z, and foo each evaluate at most once.

coffeescript faz algumas otimizações, por exemplo , armazena resultados de acesso intermediário :

typeof foo !== "undefined" && foo !== null ? (ref = foo.bar) != null ? ref.baz() : void 0 : void 0;

(Eu sinto fortemente que a verificação undefined é desnecessária para o texto datilografado: como devemos ter um var init digitado pelo texto datilografado)

+1

Nas notícias de hoje, o Dart está recebendo suporte oficial para ele: https://github.com/gbracha/nullAwareOperators/blob/master/proposal.md

Característica muito importante.

Possivelmente uma ideia inusitada, mas a geração de código para esse recurso poderia ser feita muito facilmente sem efeitos colaterais se todos decidissem que não haveria problema em lidar com o recurso com acesso à propriedade com chave:

if (aaa?.bbb?.ccc) {}

Poderia compilar para

if (__chain(aaa, "bbb", "ccc")) {}

Uma função __chain teria que ser emitida semelhante a __extends . A função __chain poderia apenas iterar pelo array arguments , retornando null quando o próximo membro não for instanceof Object ou não contiver o nome do membro. As chamadas de função podem ser tratadas passando uma matriz como parâmetro (e usando .apply() nos bastidores), então ...

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

Poderia compilar para

if (__chain(aaa, "bbb", "ccc", [1, 2, 3])) {}

Isso também manteria o JS gerado razoavelmente idiomático, mesmo para cadeias longas.

Precisa de refinamento obviamente... mas talvez haja algo aqui?

Se aaa?.bbb?.ccc? retornar o valor de a.b.c se todas as props existirem e for uma função, então não poderia

if (aaa?.bbb?.ccc?(1, 2, 3)) {}

compilar para

if (__chain(aaa, "bbb", "ccc")(1, 2, 3)) {}

?

@metaweta : Sua solução está verificando apenas void 0 ... isso não anula o objetivo do recurso?

O que dizer disso:

var result = one?.two?.three;

Gera:

var $a, $b, $c;
var result = $a = one, $b = $a ? $a.two : void 0, $b ? $b.three : void 0;

Tenho certeza que isso lida com todos os casos. Chamadas de função provavelmente precisariam de uma verificação instanceof Function .

(Pequena desvantagem aqui de variáveis ​​locais inesperadas sendo emitidas ... pode ser envolvido em um IIFE talvez)

@kevinb7 : O que acontece se ccc não for uma função? Com o código que você descreveu, __chain sempre teria que retornar uma função válida, ou um TypeError seria emitido.

@Back-io Quando uma propriedade está ausente, uma pesquisa retorna undefined === void 0. Sua solução falha ao pesquisar propriedades de valores falsey como string vazia e zero.

@metaweta : Não, não: http://jsfiddle.net/25LppbL6/

Além disso, eu imagino que a equipe TS não é louca por usar igualdade solta devido ao fato de que os linters de algumas pessoas alertam contra isso.

@Back-io Sim, tem: http://jsfiddle.net/25LppbL6/2/

@Back-io Em relação a null, não tenho certeza de qual é a semântica pretendida de a?.b. Se for "Se a propriedade b estiver definida, use-a", meu código está quase correto. A única maneira de obter null é se for atribuído como null, porque as pesquisas de propriedades inexistentes retornam indefinidas. Ele não detecta o caso em que a propriedade existe, mas está definida como indefinida. Para estar completamente correto, ele verificaria com Object.hasOwnProperty() em vez de comparar com void 0 === undefined.

Se a semântica for "se a propriedade b for verdadeira, então use-a", seu código está bem e, até certo ponto, corresponde ao idioma JS.

Umm ... a menos que eu esteja perdendo alguma coisa ... as alterações que você fez no violino apenas provam que estou certo ... var result ainda é undefined em todos os 3 casos. E não tenho certeza de qual caso você está tentando apresentar estendendo os protótipos primitivos ...

Estou bastante certo de que o comportamento do recurso seria um curto-circuito com void 0 em vez de gerar um erro. (Gosto da sua ideia acima de que deve sempre retornar void 0 no caso de um membro ausente).

Qual é a saída pretendida de ''?.toString ?

@kevinb7 : O que acontece se ccc não for uma função? Com o código que você descreveu, __chain sempre teria que retornar uma função válida, ou um TypeError seria emitido.

Bom ponto.

@metaweta : Eu imagino que a maioria das pessoas esperaria uma referência à função toString , então eu vejo onde seu ponto, ele quebra um pouco no caso em que você está acessando os membros protótipo de 0, false, ou ''.

:+1:
Angular 2 adicionou Elvis Operator à sua sintaxe de modelo

@metaweta :

Qual é a saída pretendida de ''?.toString?

Se você quis dizer ''?.toString() seria:

if ('' != null) {
  ''.toString();
}

Amostra

Talvez pudéssemos montar uma proposta espantalho para isso

@kevinb7 já existe: http://wiki.ecmascript.org/doku.php?id=strawman:existential_operator

Fiz alguns testes rápidos para implementar isso em cima de PropertyAccessExpression como seu caso especial, mas não funcionou bem, pois realmente precisamos que ?. seja associativo à direita (em vez de associativo à esquerda como . ), caso contrário, o emissor se torna desnecessariamente complexo.

Há um comentário de BrendenEich aqui que reflete isso também.

Eu fiz alguns testes rápidos para implementar isso em cima de PropertyAccessExpression como seu caso especial, mas não funcionou bem como realmente precisamos?. ser associativo à direita (em vez de associativo à esquerda como .), caso contrário, o emissor se torna desnecessariamente complexo.

@basarat você pode por favor elaborar sobre isso? Um exemplo da diferença entre associativo direito e associativo esquerdo ?. seria muito útil para mim.

@zlumer

Um exemplo da diferença entre associativo direito e associativo esquerdo ?. seria muito útil para mim.

. é associativo à esquerda, então a expressão foo?.bar?.baz se torna em AST (se tratarmos ?. da mesma forma):

                    // foo.bar.baz = PropertyAccessExpression
                    //   .expr foo.bar =  PropertyAccessExpression
                    //     .expr foo = Identifier
                    //     .name bar = Identifier
                    //   .name baz = Identifier

A emissão JavaScript necessária é

foo != null ? (ref_1 = foo.bar) != null ? ref_1.baz() : void 0 : void 0;

É apenas mais fácil fazer essa emissão (especialmente _recursivamente_) se tivéssemos o seguinte no AST:

                    // foo.bar.baz = PropertySafeAccessExpression
                    //   .name foo =  Identifier
                    //   .expr bar.baz = PropertySafeAccessExpression
                    //      .expr bar = Identifier
                    //      .name baz = Identifier

Basta pensar em _como você converteria o primeiro AST para JavaScript_ e as complexidades seriam mais claras. Espero ter ajudado :rose:

@zlumer , para adicionar ao que @basarat disse, vou apenas colocar o exemplo 1 + 2 + 3 .

Ao analisar, temos que decidir qual dessas operações acontecerá primeiro.

Se + for associativo à esquerda, isso será interpretado como ((1 + 2) + 3) .

Se + for associativo à direita, isso será interpretado como (1 + (2 + 3)) .

Você pode questionar se isso realmente faria a diferença. Em JavaScript seria! Considere o exemplo "hello" + 2 + 3 .

  • Associativo à esquerda: (("hello" + 2) + 3) => ("hello2" + 3) => "hello23"
  • Associativa à direita: ("hello" + (2 + 3)) => ("hello" + 5) => "hello5"

(Para registro, JavaScript/TypeScript usam associatividade à esquerda para o operador + .)

@basarat , meu entendimento do que @BrendanEich disse (e ele pode me corrigir se eu estiver errado - desculpe pelo ping!) é _não_ que ?. é associativo à direita, mas que a propriedade de casos especiais CoffeeScript acessa em o direito de ?. ser associativo à direita. Por exemplo, ele analisará

o.p?.q.r.s

Como

((o . p) ?. (q . (r . s))) # or something close to this

ao invés de

((((o . p) ?. q) . r) . s)

porque é mais fácil de emitir.

Nosso AST precisa prestar bem à análise semântica sã, então nosso procedimento de emissão pode ser um pouco mais complexo para atender a essa necessidade.

@basarat @DanielRosenwasser obrigado pelas explicações. Até agora eu entendo, mas ainda não tenho certeza sobre uma coisa.
O associativo à esquerda ?. é bastante óbvio e esperado:

foo?.bar?.baz

Torna-se (aprox.):

var ref = ((ref = foo) == null) ? null : ((ref = ref.bar) == null) ? null : ref.baz;

Mas eu não entendo como uma associativa direita ?. funcionaria. Por favor, você pode fornecer um exemplo?

Mas eu não entendo nada como uma associativa direita?. trabalhar. Você pode fornecer um exemplo

@zlumer O comportamento do tempo de execução será associativo à esquerda. Eu estava falando sobre o AST como DanielRosenwasser também esclareceu: is not that ?. is right-associative, but that CoffeeScript special cases property accesses on the right of the ?. to be right-associative .

Nosso AST precisa prestar bem à análise semântica sã, então nosso procedimento de emissão pode ser um pouco mais complexo para atender a essa necessidade.

@DanielRosenwasser obrigado pelo feedback :rose:

@basarat obrigado, de repente tudo ficou claro :smiley:

Semelhante ao comentário de fevereiro de @basarat , eu.... _suspiro_...

No entanto, se você pensar sobre isso, 99% dos casos de uso disso serão para verificar um ponteiro nulo para um objeto. Falando francamente, quem faz x?.b?.c quando x é um number ? Simplesmente não há muitos casos de uso na vida real para cadeias _longas_ quando _não_ estamos falando sobre um objeto (com a possível exceção de string ). Para correntes curtas, acho que podemos viver com x && x.b ou x === 0 ? null : x.b .

Então, podemos dizer que ?. só funciona em tipos de objetos? Qualquer outro tipo gera um erro de sintaxe. E não permitir chamadas de função no lado da cadeia.

Então a coisa toda transcreve para a && a.b && a.b.c .

@schungx E se um membro da cadeia for do tipo "qualquer"? Desautorizar completamente? Ou apenas deixá-lo passar e esperar o melhor?

Bem, minha sugestão? Desautorize completamente. Deselegante pra caramba, eu sei... :-)

Mas meu raciocínio:

  1. Esta é uma mão curta, então se alguém estiver usando any , use apenas uma mão longa.
  2. Se alguém está usando o TypeScript, provavelmente ele está usando para o suporte de digitação, então espero que ele não tenha muitos any por aí!
  3. any realmente deve ser manuseado com cuidado. Permitir o uso de tais short-hands com um tipo flexível como any é realmente pedir que os bugs aconteçam. Na minha opinião, any deve ser o mais limitado possível. É como o (void *) do C - o fato de você receber uma arma nuclear não significa que você deve ativá-la apenas porque pode!

Isso seria incrível operador!! Especialmente para ES6 / ES7 / TypeScript

var error = a.b.c.d; //this would fail with error if a, b or c are null or undefined.
var current = a && a.b && a.b.c && a.b.c.d; // the current messy way to handle this
var currentBrackets = a && a['b'] && a['b']['c'] && a['b']['c']['d']; //the current messy way to handle this
var typeScript = a?.b?.c?.d; // The typescript way of handling the above mess with no errors
var typeScriptBrackets = a?['b']?['c']?['d']; //The typescript of handling the above mess with no errors

No entanto, proponho uma mais clara – para não confundir? do um? b : c instruções com a?.b instruções:

var doubleDots = a..b..c..d; //this would be ideal to understand that you assume that if any of a, b, c is null or undefined the result will be null or undefined.
var doubleDotsWithBrackets = a..['b']..['c']..['d'];

Para a notação de colchetes, recomendo dois pontos em vez de um único, pois é consistente com os outros quando não são usados ​​colchetes. Portanto, apenas o nome da propriedade é estático ou dinâmico por meio de colchetes.

Dois pontos, significa que se for nulo ou indefinido, interrompe o processamento e assume que o resultado da expressão é nulo ou indefinido. (como d seria nulo ou indefinido).

Dois pontos tornam-no mais claro, mais visível e mais espacial para que você entenda o que está acontecendo.

Isso não está mexendo com números também - como não é o mesmo caso, por exemplo

1..toString(); // works returning '1'
var x = {};
x.1 = {y: 'test' }; //fails currently
x[1] = {y: 'test' }; //works currently 
var current = x[1].y; //works
var missing= x[2].y; //throws exception
var assume= x && x[2] && x[2].y; // works but very messy

Sobre os números duas opções: Sua chamada qual pode ser adotada, mas recomendo a primeira para compatibilidade com as regras existentes!

  1. Deve falhar como agora ( x.1.y == runtime error )
var err = x..1..y; // should fail as well, since 1 is not a good property name, nor a number to call a method, since it's after x object.
  1. Deve funcionar, pois entende que não é um número ligando para uma propriedade de Number.prototype
var err = x..1..y; // should work as well, resulting 'test' in this case
var err = x..2..y; // should work as well, resulting undefined in this case

Com nomes dinâmicos:

var correct1 = x..[1]..y; //would work returning 'test'
var correct2 = x..[2]..y; //would work returning undefined;

O que você acha pessoas?

A sintaxe PS foo?.bar e foo?['bar'] também funcionaria.

No entanto, usar o operador ? : e ?. pode ser muito confuso na mesma linha.

por exemplo, usando ?. e ?['prop']

var a = { x: { y: 1 } };
var b = condition ? a?.x.?y : a?.y?.z;
var c = condition ? a?['x']?['y'] : a?['y']?['z'];

ao contrário de pontos duplos .. e ..['prop']

var a = { x: { y: 1 } };
var b = condition ? a..x..y : a..y..z;
var c = condition ? a..['x']..['y'] : a..['y']..['z'];
Qual deles parece mais claro para você?

Muito interessante. :+1:

IMHO, dois pontos serão mais confusos. Existem linguagens em que dois pontos representam um intervalo (por exemplo 1..4 ) e o TypeScript pode adicionar esse recurso no futuro.

Um ponto de interrogação também tem o significado semântico de incerteza ou condicional e dois pontos não transmitem o mesmo significado.

@schungx Justo, mas ajudaria nas possibilidades estranhas como esta: a?['b'] or this a?() .

+1 ?

a?['b'] e a?() se comportam bem em coffeescript, e parecem bons para mim.

Mas, novamente, eu posso ser cego para o coffeescript.

+1 Ruby acabou de implementar o operador existencial https://twitter.com/mikepack_/status/657229703443451904. O Typescript também precisa disso, independentemente da sintaxe específica.

Isso seria tornar o código mais limpo 👍

+1 Precisa disso!

Por favor, implenente?. operador, como C# faz

+1 faz tempo que sinto falta disso

+1
tão feio de escrever: someVariable && someVariable.someMember
quando você poderia escrever: someVariable?.someMember

+1, isso seria ótimo .... (recurso mais procurado para mim)

+1, é uma boa ideia!
Só que, se o objeto for complicado, a expressão estará cheia de ?. após cada propriedade (var result=myValue?.a?.b?.c?.d?.e;) quando precisar receber um valor da última (var result=?myValue.abcde;).

+1 - Este é sem dúvida um dos maiores recursos do CoffeeScript e é de longe o recurso TypeScript mais desejado pela minha equipe depois de converter grande parte do nosso código de CS para TS.

+1 no entanto, isso é muito complexo:

var x = { y: { z: null, q: undefined } };
var z: x|y|z = x?.y?.z;

Eu gosto deste:

var x = { y: { z: null, q: undefined } };
var z: z|void = x?.y?.z;

O tipo de x?.y?.z é sempre o tipo do campo z . Claro que o tipo deve ser anulável e o valor real pode ser nulo. Se não for nulo, deve ser do tipo do campo z .

+1 Isso combinaria bem com a visão da Typescript de facilitar o desenvolvimento de projetos JS complexos de grande escala.

Alguma atualização sobre isso? É o caso da comunidade votar neste recurso para que ele seja considerado? Ou foi considerado, mas existem alguns desafios de engenharia?

Nenhuma atualização até agora porque a introdução de uma nova sintaxe em nível de expressão é perigosa sem algum tipo de proposta do comitê ECMAScript.

Consulte https://github.com/Microsoft/TypeScript/issues/16#issuecomment -57645069.

Aqui está uma discussão mais atual sobre operadores existenciais (muitos pensamentos, mas não parece muita ação):

onde devo escrever "+1" para ajudar a trazê-lo para o ES?

@msklvsk ESDiscuss

Fechando isso por enquanto. Como não há realmente nada específico do TypeScript que exija isso no nível da expressão, esse tipo de grande mudança de operador deve acontecer no comitê de especificações do ES e não aqui.

As armadilhas gerais para reavaliar isso seriam uma proposta ES concreta chegando ao próximo estágio, ou um consenso geral do comitê ES de que esse recurso não aconteceria por um longo tempo (para que pudéssemos definir nossa própria semântica e ser razoavelmente certeza de que iriam "ganhar").

a vida é uma droga

Excluindo autônomo :+1:s. Use o recurso de reações do GitHub ou envie flores e doces para o representante TC39 mais próximo.

Uma vez que me acostumei com coffeescript e Swift, não há como voltar atrás. TS está morto para mim como está atualmente.

@algesten Eu posso concordar com swift se considerarmos a linguagem em si, não tenho certeza sobre o suporte coffescript longo prazo (eu uso CoffeScript em projetos de produção). Podemos concordar em ter certeza das tendências de análise de linguagem em junho de 2016, onde podemos ver claramente o que está acontecendo com coffescript recentemente, enquanto TypeScript teve o crescimento mais rápido nos últimos anos:

TypeScript: Fora do Go ou Swift, a linguagem de crescimento mais rápido que observamos nos últimos anos é o TypeScript. O superconjunto JavaScript apoiado pela Microsoft e a fundação Angular 2 obtiveram ganhos significativos pelo segundo trimestre consecutivo, saltando de 31º para 26º. Essa foi a maior mudança em qualquer idioma do Top 30 e o segundo maior salto geral (Standard ML, 7 lugares). Na 26ª posição, na verdade, o TypeScript agora está empatado com Erlang, um lugar atrás do Powershell e quatro atrás do CoffeeScript, que está fora do Top 20. A questão que a linguagem enfrenta não é se ela pode crescer, mas se tem o impulso para quebrar o Top 20 nos próximos dois a três trimestres, superando nomes como CoffeeScript e Lua no processo.

Até 2014, as tendências coffescript eram mais do que positivas, como você pode ver aqui , foi quando o declínio começou.

Com a nova verificação nula (indefinida) estrita do typescript 2.0, é uma obrigação, porque, caso contrário, você não pode usar o encadeamento de funções!

Isso deve estar na próxima versão do ECMAScript. Considerando que isso seria ainda mais útil no JavaScript vanilla do que no TypeScript e considerando que a implementação seria naturalmente uma verificação de indefinido ou nulo em oposição a uma verificação de verdade, deve ser trivial para o TC39 adicionar.

A implementação simples e que seria idiomática em JavaScript, seria simplesmente fazer um curto-circuito ao encontrar null ou undefined e retornar undefined.

@bterlson isso faz sentido? Quais são as chances de tal proposta ser aceita?

Um dos grandes benefícios do texto datilografado é "o futuro é dado a você hoje". Esta é uma das características que me surpreendeu que ainda não estão lá.

Espero que ele seja adicionado em breve, pois o encadeamento de funções é um idioma popular e a verificação nula estrita não funciona mais, a menos que o ?. operador é adicionado.

TS 2 maravilhosa verificação nula tornou-se um bom ter que ter!

Lendo este tópico, estou meio surpreso como isso não recebe mais atenção dos mantenedores.

Em um mundo ideal, o TypeScript deveria liderar o ES e não o contrário. Sério, onde estaria o TypeScript agora se a equipe de TS sempre esperasse que o ESx propusesse e finalizasse um recurso ou uma sintaxe?

Esta sintaxe é realmente um "must have", como outros apontaram. Ele até recebeu algumas boas propostas neste segmento até agora. Acho que a implementação deve corresponder a essas expectativas.

  • Em geral, uma expressão a?.b deve ser válida em tempo de compilação se e somente se a.b for válido.
  • Ele deve avaliar cada expressão na cadeia apenas uma vez.
  • Deve estar em curto-circuito.
  • Se a execução atingir uma expressão intermediária com null ou undefined , esse valor deve ser o valor de retorno.

Quais você acha que são as partes que podem levar a divergências quando o ES especificar?

Para a?.b não vejo nenhum conflito com a sintaxe existente (e futura?). Se o analisador encontrar o token ?. , ele poderá tratá-lo como 'operador de navegação segura' (com as expectativas descritas por @cervengoc).

Conflitos de sintaxe surgem apenas ao permitir a?(b) e a?[b] , pois eles também podem ser interpretados como o início de uma expressão de operador ?: ternária. Mas, para começar, acho que isso poderia ser deixado de lado e suportar apenas a sintaxe a?.b já deixaria muitos desenvolvedores felizes!

Em um mundo ideal, o TypeScript deveria liderar o ES e não o contrário.

Em termos de sintaxe em nível de expressão, discordamos absolutamente. Há um comitê conduzindo um padrão por uma razão - não para que ações pontuais de um jogador possam decidir unilateralmente o futuro do JavaScript.

Esta sintaxe é realmente um "must have", como outros apontaram.

Se isso é obrigatório para o TypeScript, também é obrigatório para o JavaScript! Mais uma vez, leve suas preocupações ao comitê ECMAScript .

Quais você acha que são as partes que podem levar a divergências quando o ES especificar?

No mínimo, acho que haverá desacordo sobre se a sintaxe causa curto-circuito em null ou undefined ao encontrar esses valores, ou sempre em curto-circuito em undefined . Também haverá alguma controvérsia sobre se alguma forma de sintaxe entre colchetes é suportada ou não. Há também a questão de qual é o comportamento de a?.b.c . Há também a questão de ?. vs .? vs a.b? . Há a questão de qual é o efeito disso no operador delete .

O tópico do ES Discuss sobre isso tem mais de 100 comentários. Não há falta de ambiguidade! É fácil olhar para um exemplo isoladamente e pensar que não pode haver toneladas de casos de canto. Tem. Provavelmente é por isso que ninguém defendeu isso no TC39 ainda e _também_ por que não estamos nos apressando para adicionar um recurso que tem muita ambiguidade sobre como ele deve se comportar.

Peço desculpas, não li o tópico mencionado, com certeza vou dar uma olhada para ver mais.

Vemos isso um pouco diferente. Sobre o comitê, na minha opinião honesta, essa é uma das maiores razões pelas quais JavaScript nunca será _bom_. Apenas para um exemplo, muitos dos softwares de maior sucesso que eu já vi (como Total Commander ou IrfanView) são bem sucedidos porque são mantidos e projetados por UMA pessoa, e não por um *comitê". exemplo correto, mas tenho quase certeza, que se, por exemplo, você sozinho tivesse projetado o ES6 completo, então o mundo seria um lugar melhor agora.

Além disso, as ambiguidades que você mencionou são 99% _teóricas_ e meio irrelevantes do lado do desenvolvedor. Quem se importaria com o que retorna, null ou undefined ? Basta escolher um, e vamos usá-lo assim.

Em suma, você e esse comitê estão de um lado diferente da maioria de nós, e as coisas desse lado geralmente são mais complexas do que realmente são. E isso pode levar a alguma contraprodutividade, para dizer o mínimo. Não leve para o lado pessoal, mas de acordo com minha experiência em geral, algumas pessoas seriam melhores sair da sala de conferência com mais frequência e dar uma olhada em algum código.

Claro, sem ofensa, não leve nada para o lado pessoal, tenho um enorme respeito por você e toda a equipe TS, porque você revolucionou o desenvolvimento do lado do cliente de muitos desenvolvedores, inclusive eu, e obrigado por todo o seu trabalho. Estamos um pouco decepcionados com este específico, eu acho.

Um último pensamento. Eu percorri o tópico ES mencionado, e uma coisa é completamente certa: eles estão complicando demais. Eles querem projetar algo que seja bom para todos os cenários.

Eu pessoalmente ficaria completamente satisfeito e feliz com um operador de acesso condicional para membros . Não precisa de invocação condicional, assinatura de índice condicional, não precisa dar suporte a todos os cenários sofisticados que são códigos JS válidos. Isso é tudo. Mas, em vez disso, eles provavelmente continuarão sentados lá e discutirão como fazer tudo de uma vez, o que é um ótimo plano, mas não teremos nada no final do dia.

Em suma, você e esse comitê estão de um lado diferente da maioria de nós, e as coisas desse lado geralmente são mais complexas do que realmente são.

Eu não acho que você esteja refletindo o verdadeiro status de Ryan ou TC39 com precisão. Ryan e a equipe do TypeScript estabeleceram metas de design muito claras para o TypeScript. Um dos objetivos originais e ainda muito atuais é que o TypeScript seja um superconjunto de JavaScript. Não é o idioma que as pessoas gostariam que fosse (por exemplo, Dart, Haxe). Quando se trata de sintaxe, a equipe do TypeScript aprendeu da maneira mais difícil o custo de pré-inventá-lo (por exemplo, módulos). Também estamos entrando de cabeça em um desafio com membros privados de classes, onde a sintaxe ES proposta é totalmente incompatível com a sintaxe que o TypeScript usa. Por quê? Porque o que pode parecer descomplicado na superfície é impossível de alcançar devido aos desafios de tempo de execução de uma linguagem.

O TC39 "salvou" o JavaScript na minha opinião. O ES4 foi abandonado, não por falta de ideias boas e inovadoras, mas porque quebraria a internet. O TC39 entrou em forma, compartilhou e foi totalmente aberto em suas discussões e como toma decisões e nos entregou o ES2015, que é muito parecido com o ES4, mas não quebrou a internet. É incrível que tenhamos essencialmente tempos de execução de JavaScript que executam código de 10 anos atrás muito bem, mas suportam muitas melhorias significativas na linguagem. ES2016 foi a calmaria antes da tempestade. O ES2017 tem uma quantidade "razoável" de funcionalidades e mudanças e um processo de governança claro que aponta na direção certa.

Então, estar do "lado diferente" das coisas claramente funcionou, na minha opinião. O que supera a conveniência dos recursos "obrigatórios".

@kitsonk Eu não quis dizer "lado diferente" negativamente e, especialmente, não quis degradar o trabalho que foi colocado no TypeScript ou ES6. Além do mais, a melhor coisa no TypeScript IMO é que ele realmente tinha e tem um objetivo de design claro, e está bem protegido contra se tornar um caos como muitas outras coisas de código aberto.

Eu só queria dizer que esse recurso é um exemplo claro de onde um grupo de pessoas geniais acabará pensando demais e complicando demais as coisas, em vez de apenas seguir o caminho fácil e simples, e aceitar algumas limitações, como não suportar invocações ou assinaturas de índice, etc. .Alguém naquele fórum até sugeriu usar essa sintaxe nas atribuições, o que é meio louco. Ainda acho que esse fenômeno é contraproducente nesse sentido.

Eu entendo que do seu lado é uma dor que, por exemplo, membros privados tenham se tornado incompatíveis com o conceito final do ES6. Mas, por outro lado, nós TIVEMOS. Muito antes do ES6. E esse é o ponto principal do nosso lado. Grosso modo, não nos importamos como você consegue emitir o código apropriado para isso, estamos apenas usando-o com prazer. Mesmo com módulos, e tudo. Nós (ou pelo menos eu) não vimos essas dores do que você está falando, sempre ficamos felizes com membros ou módulos privados.

Esse recurso específico está no CoffeScript enquanto leio sobre ele. Por que nós, desenvolvedores simples, sempre temos que fazer concessões ao escolher uma plataforma/biblioteca/plugin, etc.? Quero dizer sempre . Isso é meio irritante. Aqui temos uma grande linguagem, que tem um grande potencial, que deixa completamente para trás todos os outros participantes (incluindo ES!), e que revolucionou com sucesso uma grande parte do desenvolvimento do lado do cliente, e quando se trata desse recurso "simples" ( Quero dizer, pelo menos a parte de acesso ao membro), ouvimos que não será implementado até que ES se comprometa com isso.

Eu queria avisar rapidamente que esse recurso passou do estágio 0 para o estágio 1 na reunião do TC39 de hoje.

Confirmação relevante: https://github.com/tc39/proposals/commit/cb447642290a55398d483f5b55fb7f973273c75d
Agenda da reunião: https://github.com/tc39/agendas/blob/master/2017/01.md

Uau! Aquilo é enorme!

Algumas "surpresas" que vejo aqui (não estou dizendo que discordo, apenas coisas que provavelmente teríamos feito diferente se tivéssemos feito isso antes):

  • null não é produzido a partir de uma expressão a?.b : quando a é null isso produz undefined
  • ~ Propagação em pontos encadeados: a?.b.c.d não será lançado se as propriedades b e c forem undefined ~ Ryan não puder ler
  • ~Propagação na presença de parênteses : mesmo (a?.b).c não será lançado se b for indefinido~ Ryan não consegue ler
  • ~A propagação ocorre mesmo em chamadas de método: a?.b.c().d retornará undefined se a invocação c retornar null ~ Ryan não puder ler
  • O operador delete é suportado
  • A sintaxe de colchetes a?.[x] é suportada
  • A sintaxe de chamada de função func?.(...args) é suportada, mesmo para chamadas que não sejam de método (!)

Eu esperaria ver mudanças nessas áreas entre agora e o estágio 2.

Acho que o coffeescript acertou.

a?.bc lança se b é indefinido.

a?() e a?[0] são ambos bons.

  • Propagação em pontos encadeados: a?.bcd não será lançado se as propriedades b e c forem indefinidas
  • Propagação na presença de parênteses: mesmo (a?.b).c não será lançado se b for indefinido
  • A propagação ocorre mesmo em chamadas de métodos: a?.bc().d retornará undefined se a invocação c retornar null

Esses pontos não me parecem precisos. Da proposta:

a?.b.c().d      // undefined if a is null/undefined, a.b.c().d otherwise.
                // NB: If a is not null/undefined, and a.b is nevertheless undefined,
                //     short-circuiting does *not* apply

Uau, eu interpretei isso totalmente errado. Você está certo. Atualizando

@algesten da proposta original:

a?.()

b?.[0]

doce. o operador pode ser considerado ?. então.

Há algumas conversas adicionais acontecendo aqui: https://github.com/estree/estree/issues/146

Uma citação que se aplica bem: «Torne as coisas simples fáceis e as difíceis possíveis ». Assim, talvez, suporte bem os casos mais comuns, enquanto ignora (pelo menos inicialmente) os casos complicados/raros, enquanto eles ainda podem ser "feitos manualmente" com sintaxe mais longa (existente).

Apenas meus dois centavos

let a = b?.c?.d?.e;

para:

let a;
try{
   a = b.c.d.e;
}catch(e){
   a = undefined;
}

@cedvdb semântica totalmente diferente - exceções lançadas em getters não devem causar coalescência

@RyanCavanaugh sim .. Eu não pensei nisso.

Isso está no radar para implementação agora, ou a equipe de TS esperará que a proposta de ES avance?

Está na nossa lista curta; ainda temos algumas perguntas/preocupações, mas você deve ver um movimento sobre isso no próximo mês.

Não tenho certeza de onde veio o comentário de @mhegazy - o número de perguntas em aberto sobre a proposta TC39 é muito grande para que possamos fazer um trabalho significativo aqui. Especificamente, questões sobre como null e undefined interagem e qual sintaxe é realmente suportada precisam ser abordadas primeiro. O estágio 2 é o mínimo absoluto e preferimos o estágio 3 devido ao impacto no comportamento do tempo de execução.

este código simplesmente funciona?

a == undefined ? expression : undefined

expression significa ax, a[x], a(x), delete também pode ser gerado aqui

então a?.b?.[c]?.(d) irá gerar

a == undefined ? (a.b == undefined ? (a.b[c] == undefined ? a.b[c](d) : undefined) : undefined) : undefined

parece vai passar por toda a regra de RyanCavanaugh


se odeia o operador == , também pode ser a === undefined || a === null

@zh99998 você _tem_ que odiar == porque '' e 0 também são equiparados. Quase se argumenta que o comportamento do tempo de execução deve ser algum tipo de verificação em (typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol') && value !== null , que agora está ficando bastante complexo.

Como @RyanCavanaugh disse, é improvável que progrida até que a proposta TC39 progrida para pelo menos o estágio 2 ou 3.

Vejo == igual null e undefined como
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

e teste passado no console chrome:

'' == undefined
false
0 == undefined
false

@kitsonk undefined apenas coage para null e vice-versa. Nenhum outro valor coage para indefinido ou nulo.

Você parece estar confuso com valores falsos. 0 e "" são realmente falsos, mas nunca coagirão a nulo nem indefinido.

== implica coerção. null == 0 é falso porque nada, exceto indefinido, pode forçar a nulo. O mesmo vale para undefined == 0 .

Outro exemplo poderia ser

    if(!NaN) console.log("NaN is falsy") // NaN is falsy
    if(false == NaN) console.log("NaN coerces to false")
   else console.log("NaN doesn't coerce to false");// NaN doesn't coerce

Você começa a imagem.

Vejo muitas tentativas usando operadores ternários, mas qualquer implementação que se refira à mesma propriedade várias vezes pode resultar em efeitos colaterais inesperados.

Felizmente, JavaScript tem IIFEs, então você pode armazenar o resultado de um acessador em um parâmetro de função, consultá-lo o quanto quiser e nunca avaliá-lo mais de uma vez. No meu exemplo abaixo, crio um lambda coalesce que pode ser chamado várias vezes no lugar de uma expressão contendo operadores ?. .

Uma coisa com a qual os implementadores de linguagem precisariam se preocupar são chamadas de array e função no meio de uma expressão. Acho que o compilador poderia lidar com a detecção dessas situações e simplesmente anexar essas expressões ao final da chamada coalesce.

const coalesce = (x: any, y: string) => x == null ? null : x[y];

const x = {
    y: {
        z: "Hello, World!!!"
    },
    f: () => "Foo!",
    a: ["Array!"]
};

// x?.y?.z
const value1 = coalesce(coalesce(x, 'y'), 'z');

// x?.f()
const value2 = coalesce(x, 'f')()

// x?.a[0]
const value3 = coalesce(x, 'a')[0]

Observe que não há necessidade de um global real chamado "coalesce". Esse código pode ser embutido diretamente em qualquer expressão. Pode reduzir o inchaço dando-lhe um nome, no entanto.

Não estou muito preocupado com a sintaxe usada para o operador ou se os implementadores da linguagem querem esperar por um padrão. Apenas pensei em mostrar outra abordagem.

Isso também traz a necessidade de um operador ?? . Em C#, isso substitui qualquer expressão null pelo que estiver à direita.

string x = null ?? "Hello";
````

In JavaScript, it is more idiomatic to use `||` to replace "falsey" values with the value on the right. 

```javascript
var x = null || "Hello";

Infelizmente, a veracidade captura muitos casos extremos ( 0 , false , etc.). Trabalhando com um operador nulo coalesce ( ?. ), você desejaria algo específico para null -ness.

const x = { y: "" };
const result1 = x?.y || "default";  // I'd expect "default"
const result2 = x?.y ?? "default";  // I'd expect "" 

@jehugaleahsa , no seu exemplo com coalesce, teria que haver uma maneira de impedir que chamadas de função e acesso a membros acontecessem se a verificação anterior retornasse nula. No exemplo x?.f(), f não deve ser chamado se x for nulo.

@bschlenk Acho que não concordo. Acho que deve falhar com uma mensagem como null is not a function , mas isso não depende de mim, eu acho.

O recente aumento nos comentários detalhando possíveis maneiras de implementar o operador é um pouco estranho.
A implementação é provavelmente um problema resolvido.

Se você estiver interessado, há a biblioteca idx , que se assemelha muito ao comportamento do operador ?. , embora também ignore muitos detalhes do operador planejado. De qualquer forma, suas especificações para a saída de compilação podem ser de interesse para quem se pergunta sobre as maneiras pelas quais essas coisas podem ser implementadas.

O TS pode produzir algo assim ou pode produzir algo completamente diferente, mas não acho que seja isso que estamos esperando aqui. Já foi dito muitas vezes aqui que o TS não conseguirá o operador até que a proposta do ES se mova para uma direção ou para outra.

É a semântica que ainda tem algumas incógnitas, listadas aqui e aqui .

Tenha certeza de que estaremos implementando isso corretamente e não precisamos de mais 100 comentários aqui para descobrir o que || e ? ... : ... fazem. Como @noppa observou, estamos apenas aguardando a finalização da especificação ES.

https://github.com/babel/babel/pull/5813 (acompanhando o PR da babilônia) acabou de ser incorporado ao preset-stage-1 da Babel. A especificação ainda está no estágio 1, é claro, mas isso ajudará a avançar.

Talvez eu esteja errado, mas não vi nenhum link óbvio para a proposta tc39 neste tópico, então aqui está para os futuros leitores: https://github.com/tc39/proposal-optional-chaining

O encadeamento opcional FYI estará no TC39 na próxima semana https://github.com/tc39/agendas/blob/master/2017/07.md

@jehugaleahsa Acho que você está certo e estarei apresentando ?? no TC39 na próxima semana. Você pode acompanhar a proposta aqui: https://github.com/gisenberg/proposal-nullary-coalescing

Vejo que o encadeamento opcional foi coberto no TC39... Qual foi o veredicto?
Espero que tenha sido o suficiente para avançar isso no Typescript 😉

@markwhitfeld Do resumo das notas :
Operadores de encadeamento opcionais: permanece no estágio 1, voltará mais tarde com definições mais claras para várias opções e respostas aos comentários

Notas completas aqui: https://github.com/rwaldron/tc39-notes/blob/master/es8/2017-07/jul-27.md#13iia -optional-chaining-operator

Ainda há questões em aberto sobre como o operador deve se comportar, então parece que ainda não está em um estado em que possa ser adicionado ao TypeScript.

A partir dessas notas, parece que o comitê não tem ideia de como as opções apresentadas funcionam, eu pensei que eles teriam aproveitado para conhecer as propostas que estavam planejando discutir, antes de discuti-las. Acho que vai demorar um pouco mais até que eu possa habilitar verificações nulas estritas.

Eu espero muito que eles vão com a opção 2/4 (que é o estado atual da proposta de qualquer maneira).

15 de julho de 2014 - 4 de setembro de 2017, nada ainda

@frankfvb você claramente não leu o problema.

Houve muita discussão que levou a equipe principal a acreditar que seria imprudente implementar neste momento até que haja mais progresso na proposta ECMAScript que impactaria diretamente a funcionalidade desse recurso no TypeScript.

Desde a última reunião do comitê de padrões ECMAScript, a proposta permanece na Fase 1 , pois tem algumas questões muito fundamentais sobre como seria implementada. Embora não seja uma regra rígida e rápida, o TypeScript implementará apenas as propostas do Estágio 3. Às vezes, ele implementa propostas do Estágio 2 se eles acreditam que é de importância crítica e potencialmente o uso no TypeScript impulsiona a evolução do padrão.

Não tenho certeza de como as pessoas mais claras podem ser sobre isso.

Como eu disse anteriormente , isso está na nossa lista curta. estamos esperando que o TC39 chegue a algum tipo de consenso sobre a semântica do operador. nós odiaríamos lançar isso e depois quebrar os usuários.

Este não é o tópico para refazer a discussão do TC39

Se quiser ponderar na proposta, vá comentá-la no local apropriado . Eu também tenho opiniões, mas colocá-las neste tópico não vai fazer nada.

Criei algo simples que atende às minhas necessidades atuais. Ele só funcionará em uma cadeia em que cada link é um nome de propriedade, portanto, o acesso a um elemento em uma matriz (por exemplo) não é suportado.

Implementando um operador Elvis realmente simples no TypeScript

Além disso, se você tiver lodash/underscore, já pode usar _.get(Book, 'author.name.firstName') que fará o que isso quer

Edit: Aparentemente, este é um mau conselho por causa de problemas de tipo com o método _.get() . Veja o comentário abaixo

@tolgaek , _.get tem digitações ruins, mesmo com essas tipagens melhores ( ainda não mescladas , por causa dos autores) o typescript pode definitivamente deduzir o tipo de resultado apenas se a profundidade do objeto for 1, em todos os outros casos é any e deve ser verificado em tempo de execução

Por outro lado, com o operador elvis, o texto datilografado pode ser capaz de inferir o tipo de resultado em objetos com qualquer profundidade, é por isso que estou ansioso pelo operador elvis

oh entendo, eu não sabia que havia problema de digitação. Obrigado @BjornMelgaard

@mhegazy esse recurso não pode ser implementado primeiro e marcado como recurso experimental? Eu acho que as pessoas não devem ter problemas se houver alterações nas especificações do recurso experimental.

Elvis não se diverte esperando tanto tempo.

Aterrissou em Babel7. Texto datilografado, estamos lentos. Alguém, por favor, faça isso acontecer.
:)

@gs-akhan o plugin Babel implementa uma versão antiga da proposta de alguns meses atrás. Houve mudanças na proposta desde então (incluindo uma mudança significativa na forma como o operador é analisado), e provavelmente haverá mais mudanças antes que o recurso atinja o estágio 2 (muito menos o estágio 3), portanto, qualquer código escrito com o babel atual plugin pode quebrar quando o recurso real for lançado. O Babel implementa intencionalmente os recursos propostos antes que eles fiquem estáveis ​​para que os autores das especificações e outras partes interessadas possam experimentar o recurso conforme proposto. Só porque o Babel implementou um recurso não significa que ele possa ser implementado de uma forma que não exija mudanças importantes no futuro.

@alangpierce Isso faz sentido. Obrigado

Entendo que este é um operador muito, muito bom, mas tê-lo disponível antes que suas regras sejam resolvidas é uma arma de fogo e não vamos fazer isso. O comportamento de tempo de execução do operador ainda está em fluxo; se você escrever código hoje, ele pode parar de funcionar amanhã de maneiras que não são imediatamente aparentes - talvez falhas raras, talvez corrupção de dados, quem sabe? Um pouco de paciência agora vai lhe poupar muita dor daqui a alguns meses.

Começando a pensar que este ticket deve ser bloqueado (não fechado) até que a especificação esteja pronta.

Quando a especificação estará pronta?

@oliverjanik A especificação de rascunho atual pode ser encontrada aqui . Há um item de agenda para avançar a proposta para o estágio 2 na próxima reunião do TC39 (26/09 a 28/09). Eu estarei apresentando esses slides naquele momento. Para pessoas que gostariam de revisar antecipadamente e fornecer feedback, registre problemas no repositório de propostas .

Muito obrigado @gisenberg por defender essa questão para nós! Eu estava pensando em montar um conjunto de slides resumido para ajudar a esclarecer as opções em torno do operador que poderiam ser usadas na reunião do TC39 para evitar confusão, mas você já fez isso. Otimo trabalho!
Talvez apenas uma outra coisa que possa ser benéfica para a conversa do TC39 (e o conjunto de slides) seja observar a semântica e a sintaxe do operador em outros idiomas. Embora outras linguagens não devam necessariamente ditar como deve funcionar em Javascript, seria útil manter o operador semelhante ao de outras linguagens para evitar confusão.
Boa sorte para a próxima semana!!!

Desculpe por sair um pouco do tópico mais uma vez, mas imaginei que algumas pessoas aqui poderiam achar interessante que no Flow agora é possível adicionar definições de tipo de trabalho para funções getter seguras como _.get .

Exemplo: flowtype.org/try

Não é o código mais bonito de todos os tempos e não distingue corretamente entre nulo e indefinido, mas fora isso parece funcionar muito bem.

AFAIK a única coisa que falta no TS para fazer o mesmo é algo assim $NonMaybeType .
Não que isso eliminasse a necessidade desse operador, claro, só achei legal.

Isso não atingiu o Estágio 2 na última reunião do TC39 devido a preocupações sobre a consistência sintática em relação ao acesso de colchete vs ponto vs chamada e comportamento semântico (previsibilidade em torno dos efeitos colaterais das expressões à direita de um undefined / null -operando avaliado), bem como perguntas abertas sobre que tipo de expressões podem ser não-op'd (por exemplo x.?b() quando x.b é um number )

(vote 🎉 neste comentário para jogar fruta podre)

Obrigado por nos informar. Que chato. Talvez o escopo precise ser reduzido para que seja mais simples, mas ainda útil?

Talvez o escopo precise ser reduzido para que seja mais simples, mas ainda útil?

Este é o desafio que o TC39 enfrenta, que, embora chato, tenho que admitir que estou feliz que as pessoas estejam passando por isso. É muito difícil para eles introduzirem uma sintaxe de nível bastante complexa e, de fato, nesse caso, a digitação fraca da linguagem está causando uma quantidade significativa de casos extremos que precisam ser resolvidos, ou você obtém código que vai 💥 o que é não é bom para ninguém. Eu não acho que se você introduzir um operador como esse, você pode realmente restringir seu escopo. Seria mais fácil para os implementadores cobrirem 90% dos casos, mas não acho que ¯\_(ツ)_/¯ seja um código válido para os 10% restantes.

Pessoal, mais de 3 anos depois, eu diria que é hora de dizer "foda-se" para o comitê e fazer do nosso jeito, o que for idiomático para o TypeScript. Esse recurso não pode funcionar corretamente sem digitação estática.

Faça uma implementação do TypeScript com uma sintaxe que seja explicitamente incompatível com a proposta do TC39. Depois que o ES obtiver um operador de navegação segura, o TypeScript terá duas opções: uma com semântica de merda compatível com ES e outra vinculada ao sistema de tipos. Isso significa que você não pode usar o operador safe-nav TS com qualquer "tipo", e tudo bem.

@notsnotso Typescript não deve quebrar o JS, é para isso que serve o Coffeescript.

Talvez uma boa solução seja:

  • implementar muito simples? operador para desenvolvedores impacientes, como recurso experimental (como decorador), com aviso - ele quebrará seu código no futuro
  • espere mais 3 anos quando o padrão for escrito, implemente como recurso não experimental. Escreva tutorial, o que poderia ser quebrado. Mesmo, com a tipagem estática, é possível escrever avisos quando as pessoas compilam o código com a implementação do novo operador.
    "faça do nosso jeito, o que for idiomático para o TypeScript" não é um caso, porque em 3 anos as pessoas vão se deparar com o problema, que ts elvis não funciona da mesma maneira que em js.

Não estou surpreso que isso não tenha acontecido, para ser honesto. No meu original
post, apontei a ambiguidade em relação à indexação, chamadas de função, etc.
Honestamente, quanto mais eu penso em ?. , mais parece que não
pertencem em JavaScript, pois tem undefined .

Eu preferiria um utilitário de propósito mais geral que resolvesse esse problema
e mais. Um pensamento que tive foi algo como uma linha try/catch
expressão ( let x = try a.b.c else 0 ) em conjunto com um operador que
verificado para "nully" (por exemplo, x ?? 1) em vez de "falsey" (por exemplo, x || 1).
Então, você os combinaria assim: try a.b.c ?? 0 else 0 . É prolixo, sim,
mas basicamente diz: Tente avaliar abc e se o resultado for null ou
undefined , retorna 0. Se a ou b for undefined e uma exceção for lançada,
pegá-lo e retornar um 0.

Uma alternativa seria tornar a cláusula else opcional, padronizando
undefined . Então você poderia escrever a expressão como: let x= (try a.b.c) ?? 0 . Isso é muito compacto, evita a ambiguidade e fornece uma visão mais
solução de propósito geral que pode resolver outros problemas.

Não estou dizendo que devemos pressionar por isso; tudo o que estou dizendo está lá
são alternativas para ?. e devemos realmente explorar nossas opções até
encontre um que se encaixe bem com a linguagem JavaScript.

Na quinta-feira, 5 de outubro de 2017 às 7h51, Dmitry Radkovskiy [email protected]
escrevi:

@notsnotso Typescript não deve quebrar JS, é isso que
Coffeescript é para.


Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/Microsoft/TypeScript/issues/16#issuecomment-334441781 ,
ou silenciar o thread
https://github.com/notifications/unsubscribe-auth/ABTgPilbZfuKc2egdBrYfdTHHeDl3F6Sks5spMLLgaJpZM4CNapf
.

@zlumer boa piada. Dito isso, um recurso específico do TS não quebraria o Javascript.

@jehugaleahsa Eu gosto da sua proposta, e ela é encontrada em outros idiomas. Eu acho que funcionaria muito bem para o TS.

Eu direi que realmente não entendo a forte necessidade de esperar até que o ECMAScript aceite o operador. O TypeScript adicionou classes, módulos, lambdas, tudo muito antes de chegarem ao ECMAScript. Na verdade, um dos objetivos declarados do TypeScript foi/é testar recursos JS experimentais. O TypeScript fornecendo sua própria implementação sem dúvida ajudaria a informar essas discussões no comitê.

Vocês estiveram dispostos a fazer mudanças importantes antes, quando o comitê eventualmente seguir uma direção diferente, eu realmente não vejo por que esse recurso é tão diferente.

async/await também foi introduzido ao TypeScript em um estágio inicial.

Dito isso, um recurso específico do TS não quebraria o Javascript.

Não haverá algo como "recurso específico do TS", verifique as metas de design do TypeScript para obter mais informações.

O TypeScript chega antes do ES indo para um caminho brilhante, pode haver alguns existentes,eles são mantidos apenas para compatibilidade.

O TypeScript adicionou classes, módulos, lambdas, tudo muito antes de chegarem ao ECMAScript.

E os módulos em particular são um grande desastre que ainda causa confusão sem fim. Esse é um exemplo que a equipe do TypeScript usou repetidamente como um exemplo de _erro_ e se precipitando cedo demais. Também temos campos privados surgindo agora, o que, novamente, tenho sido grato por private nos últimos 5 anos, mas não causará confusão sem fim.

Novamente, os decoradores estão disponíveis sob a bandeira, mas agora que os decoradores estão realmente chegando ao Estágio 3, será necessária uma reimplementação e quebra de código para o TypeScript. É por isso que essas coisas _não_ podem ser tomadas como prováveis.

async/await também foi introduzido ao TypeScript em um estágio inicial.

Uma vez que a proposta ECMAScript atingiu a Fase 3.

Ninguém está se comportando mal, mas se essa conversa não puder ir a lugar algum, exceto em círculos (e se houvesse uma bandeira? Sim, estamos cientes das bandeiras) ou fora do tópico (e se TS deixar de ser JS? Não, cinco anos depois, não estamos mudando nossa mente nisso), não precisamos tê-lo. Dissemos há cerca de 3 anos que implementaremos isso exatamente quando o comitê ES bloquear sua semântica.

Mais uma vez, o repositório da proposta é https://github.com/tc39/proposal-optional-chaining e você pode acompanhar seu progresso lá. Estaremos trabalhando offline para tentar melhorar as chances da proposta na próxima reunião do TC39 porque queremos muito que isso também seja aprovado.

Atualização: Chegamos ao estágio 2 esta tarde no TC39!!!

O encadeamento opcional é o Estágio 3

Desbloqueando brevemente este apenas para fins comemorativos

Viva!

Não faça spam...

Você pode enviar um emoji expressando seus sentimentos

Não faça spam...

Você pode enviar um emoji expressando seus sentimentos

Além disso, é divertido ver a contagem de emojis subir assim. Eu nunca vi um comentário de problema ganhar tanta popularidade tão rápido!

Gravei um pequeno vídeo das atualizações em tempo real https://youtu.be/JLBrgPjeGhc

Alguém pode me descadastrar dessa coisa?

@DanielRosenwasser Caso você não esteja brincando, ou para qualquer outra pessoa que queira cancelar a inscrição e não saiba como, você está procurando este botão na barra lateral direita:

image

Sem falar que há um link no e-mail:

image

Foi uma brincadeira, estou trabalhando na proposta.

@RyanCavanaugh depois de desbloquear este problema:
martian

Muito animado para ver isso finalmente pousar! 🎈 🎉

Mal posso esperar pela correção rápida do VSCode correspondente 😆

image

Estou indo para @ @RyanCavanaugh porque ele provavelmente não está inscrito neste tópico e eu quero ser rude! (e @DanielRosenwasser para uma boa medida)

@kitsonk Não seja burro. As pessoas são livres para cancelar a inscrição e não serem assediadas.

@kitsonk , Por que RyanCavanaugh ou DanielRosenwasser seriam cancelados neste tópico? Ryan desbloqueou este problema e Daniel respondeu três comentários acima de você.

Mesmo que eles tenham sido cancelados, não há necessidade de causar mais fadiga de notificação enviando spam para eles.

Aparentemente o GitHub é um lugar terrível para humor sarcástico, caramba...

Muito obrigado aos nossos campeões do TC39 por descobrir os detalhes desagradáveis ​​desse novo recurso de linguagem!

thanks

Acho que @kitsonk estava apenas brincando, assim como eu meio que estava. Enquanto estamos sendo um pouco bobos com a emoção, este é um lembrete gentil para manter as coisas civis de acordo com o CoC.

@Daniel Rosenwasser
Eu poderia ter que tentar isso? Ou @rbuckton tem mais um ramo para ele 🙋🏻‍♂️


Ok, entendi https://github.com/microsoft/TypeScript/commits/optionalChainingStage1 🤦🏻‍♂️

Sim, isso é bastante desatualizado, infelizmente.

Acabei de perceber que esta edição foi aberta há 5 anos. :atônito:

@rbuckton

Sim, isso é bastante desatualizado, infelizmente.

Eu poderia tentar isso?

Desculpe @jhpratt @MatthiasKunnen , esqueci que nem todos compartilhamos o mesmo contexto no GitHub. Eu tenho saltado por aqui por um longo tempo e passei um tempo com Ryan e Daniel IRL e participei brevemente do evento recente que deu origem à minha piada interna incompreendida. Desculpas.

Toda esta edição, porém, mostra uma interessante arqueologia dos princípios de design do TypeScript. Ryan o levantou em um momento em que o TypeScript _poderia_ realmente considerar a sintaxe que antecedeu a consideração séria no ECMAScript. Foi nessa época que o TypeScript internamente estava aprendendo algumas lições de previsão da sintaxe do ES2015 que estavam causando sérios impactos na linguagem. A equipe decidiu não considerar a inclusão até que houvesse uma proposta do TC39 Estágio 3.

Embora @RyanCavanaugh possa resolver isso, e eu sei que ele esteve perto do que aconteceu com a proposta, suspeito que seria improvável que o que a equipe teria implementado em 2014 fosse 100% compatível com a proposta atual do Estágio 3 . Portanto, embora certamente seja uma celebração, também seja grato por não termos 5 anos de código TypeScript com um operador de navegação seguro que não seria totalmente consistente com o comportamento da proposta.

🙏

westwing

Este é o momento de remover o rótulo Waiting for TC39 ? 🤔

Este é o momento de remover o rótulo Waiting for TC39 ? 🤔

E adicione ship-it

uau uau

Finalmente. Literalmente estava perguntando sobre o encadeamento opcional y'day, imaginando quando será o estágio 3, e bam! Obrigado a todos que estavam trabalhando para passar por isso!

Como silenciar este tópico? :)

@opnksyn Se você não se importa com o entusiasmo do spam, há um recurso para ser notificado quando esse problema for encerrado. Isso impedirá que todos os comentários apareçam em notificações como a que você acabou de fazer 😄

image

image

Alguma solução de emissão já definida?
Algo assim pode ser interessante:

function __chain<T extends object, U>(value: T|null|undefined, callback: (value: T) => U): U|undefined {
    if (value !== null && value !== undefined) {
        return callback(value);
    }

    return undefined;
}

type Foo = { x?: { y?: { z?: () => number } } }

const foo: Foo|null = { }

// foo?.x?.y?.z?()
const value = __chain(foo, _a => __chain(_a.x, _b => __chain(_b.y, _c => __chain(_c.z, _d => _d()))));

Eu prefiro o que o Babel faz, pois evita criar escopos de função desnecessários:

var _foo, _foo$x, _foo$x$y, _foo$x$y$z;

// foo?.x?.y?.z?.()
(_foo = foo) === null || _foo === void 0 ? void 0
    : (_foo$x = _foo.x) === null || _foo$x === void 0 ? void 0
    : (_foo$x$y = _foo$x.y) === null || _foo$x$y === void 0 ? void 0
    : (_foo$x$y$z = _foo$x$y.z) === null || _foo$x$y$z === void 0 ? void 0
    : _foo$x$y$z.call(_foo$x$y);

Eu prefiro o que o Babel faz, pois evita criar escopos de função desnecessários:

var _foo, _foo$x, _foo$x$y, _foo$x$y$z;

// foo?.x?.y?.z?.()
(_foo = foo) === null || _foo === void 0 ? void 0
  : (_foo$x = _foo.x) === null || _foo$x === void 0 ? void 0
  : (_foo$x$y = _foo$x.y) === null || _foo$x$y === void 0 ? void 0
  : (_foo$x$y$z = _foo$x$y.z) === null || _foo$x$y$z === void 0 ? void 0
  : _foo$x$y$z.call(_foo$x$y);

Concordo, @ExE-Boss. Eu sinto que evitar a criação de escopos de função desnecessários é o ideal (mesmo que torne o código compilado um pouco feio)

Desempenho e aderência à especificação ES devem definitivamente vencer a legibilidade quando estamos falando de código compilado.

Existe alguma razão para o código compilado pelo Babel se comparar a null e void 0 com triplo igual em vez de um simples == null ?

Existe alguma razão para o código compilado pelo Babel se comparar a null e void 0 com triplo igual em vez de um simples == null ?

@proteria Acabei de dar uma olhada rápida no código de encadeamento opcional para __Babel__ ; parece que se você passar a opção loose e configurá-la para um valor verdadeiro, ela não produzirá as verificações de igualdade estritas

Isso é por causa de document.all (ou para ser pedante, o slot interno [[IsHTMLDDA]] ), uma peculiaridade que recebe tratamento especial na linguagem para compatibilidade com versões anteriores.

document.all == null // true
document.all === null || document.all === undefined // false

Na proposta de encadeamento opcional

document.all?.foo === document.all.foo

mas document.all == null ? void 0 : document.all.foo retornaria incorretamente void 0 . No modo solto, esse detalhe da especificação é descartado por simplicidade/desempenho/tamanho do código gerado, pois a maioria das pessoas não precisa lidar com document.all qualquer maneira.

Certamente o caso document.all poderia ser especial? Não deve exigir muito código extra, apenas algumas linhas para verificar o objeto e a propriedade.

Exceto que document.all pode ser atribuído a uma variável, e rastrear onde ela é usada requer um sistema de tipos, e é por isso que o Babel produz por padrão:

(_prop = prop) === null || _prop === void 0 ? void 0 : _prop./* do stuff */;

Eu estou ciente disso. Babel não tem um sistema de tipos, o TypeScript tem. Talvez não seja tão simples como eu fiz parecer, mas imagino que já exista código para certas situações que seja capaz de rastrear o uso.

Na verdade, você não precisa de um sistema de tipos para rastrear variáveis document.all , já que o comportamento especial está em HTMLAllCollection , não document.all .

Então você deve ser capaz de fazer uma verificação instanceof HTMLAllCollection , e você será ouro.

Sim, mas... por que você fez instanceof quando você pode apenas fazer === null || === void 0 ? Certamente isso é mais simples.

Com certeza - eu estava apenas apontando que você não precisa de um sistema de tipos para rastrear document.all :)

Pessoalmente, estou tentado a dizer apenas quebre e veja quem reclama, mas está nas especificações, então é mais fácil ficar com isso.

@noppa Pode ser executado em tempo de compilação. Se foo instanceof HTMLAllCollection for true , emit foo === null || foo === void 0 , caso contrário podemos _safely_ emitir foo == null .

@G-Rath Goste ou não, obsoleto não significa que não deva funcionar. O TypeScript deve permanecer compatível com JavaScript.

@jhpratt Mas isso atualmente vai contra os Non‑Goals do TypeScript Design .

Além disso, você ainda teria que fazer foo === null || foo === void 0 para qualquer coisa à qual HTMLAllCollection pudesse ser atribuído, por exemplo. any ou object , então acho que não vale a pena.

Presumo que você esteja se referindo ao não-objetivo (5)

Adicione ou confie em informações de tipo de tempo de execução em programas ou emita código diferente com base nos resultados do sistema de tipos. Em vez disso, incentive padrões de programação que não exijam metadados de tempo de execução.

Embora eu concorde que isso certamente emitiria um código diferente com base no tipo, é apenas para reduzir o tamanho do código. Embora, como você apontou, não seja tão simples quanto verificar HTMLAllCollection .

Para ser justo, TS _has_ rejeitou um potencial minificador que usa informações de tipo, e isso é (mais ou menos) relacionado - o principal motivo para emitir == null é reduzir o tamanho do código com base nas informações de tipo.

Se isso _não for_ implementado, seria ótimo se a equipe lang adicionasse uma opção ao tsconfig semelhante à opção "solta" do Babel.

Depois de verificar rapidamente, o terser converte automaticamente foo === null || foo === undefined em foo == null , o que não é seguro devido a esse caso extremo.

Se isso não for implementado, seria ótimo se a equipe lang adicionasse uma opção ao tsconfig semelhante à opção "solta" do Babel.

Da mesma forma, muitos de nós usam o TypeScript para ferramentas de construção e para aplicativos móveis, nenhum dos quais precisa se preocupar com as restrições do navegador!

Na verdade, usamos TS e Babel juntos, então talvez uma dessas opções seja passar o operador para o Babel/tempo de execução subjacente!

@fbartho

Na verdade, usamos TS e Babel juntos, então talvez uma dessas opções seja passar o operador para o Babel/tempo de execução subjacente!

Não entendo este comentário. Você não precisa de nenhuma opção extra para passar o operador para o Babel; se você tiver o TypeScript configurado para o Babel, você já terá noEmit: true que já passa _tudo_ para o Babel.

A implementação do TypeScript do @Zarel Babel está faltando vários recursos nos quais nossa base de código já estava confiando, incluindo namespaces e const enums. Estamos usando TSC com emissão habilitada e aplicando Babel como uma segunda transformação. (Estamos trabalhando para nos livrar dos namespaces, mas não está claro se conseguiremos nos livrar de todos os recursos incompatíveis)

As pessoas que chegam a este tópico devem começar no anúncio do estágio 3 anterior e ler os comentários a partir daí (culpe o GitHub por ocultar toneladas de conteúdo do usuário sem uma maneira direta de carregar tudo)

Ótimo recurso - "encadeamento opcional" / "navegação segura". Especialmente no modo estrito do TypeScript. Impressionante saber que isso será implementado em breve. ❤️

Isso me trouxe até aqui e espero que seja apoiado. Apenas um caso de uso:

Esperado no TypeScript 3.7.

document.querySelector('html')?.setAttribute('lang', 'en');

VS

Atualmente no TypeScript 3.5.

const htmlElement = document.querySelector('html');
if (htmlElement) {
  htmlElement.setAttribute('lang', 'en');
}

Funcionará sem erros? Ou ainda é um TypeError: Cannot read property 'setAttribute' of null. ? O ? op. deve ser cancelado outras cadeias após null / undefined.

class Test {
  it() {
    console.log('One');
    document.querySelector('html')?.setAttribute('lang', 'en');
    console.log('Two');
  }
}
new Test().it();

espero o seguinte:
Se o elemento html não existir (null). O console deve registrar One e Two , e o método setAttribute não deve ser invocado. (Sem erros).
Eu entendi isso corretamente?

@domske FYI, isso não é estritamente um recurso TS; é um recurso JS.

De acordo com a proposta do TC39 a sintaxe será:

document.querySelector('html')?.setAttribute?.('lang', 'en');

A discussão começou a circular novamente, então voltamos ao estado bloqueado.

Eu realmente imploro a qualquer pessoa tentada a deixar um comentário em um tópico do GitHub com mais de 100 comentários para realmente se comprometer a ler todos os comentários anteriores primeiro. Provavelmente sua pergunta, e a resposta para ela, será encontrada lá!

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

Questões relacionadas

manekinekko picture manekinekko  ·  3Comentários

bgrieder picture bgrieder  ·  3Comentários

remojansen picture remojansen  ·  3Comentários

jbondc picture jbondc  ·  3Comentários

wmaurer picture wmaurer  ·  3Comentários