Typescript: Sugerencia: "operador de navegación segura", es decir, x?.y

Creado en 15 jul. 2014  ·  205Comentarios  ·  Fuente: microsoft/TypeScript

Estado actual

  • La propuesta TC39 ahora está en la etapa 3 (🎉🎉🎉🎉🎉)
  • La implementación está en progreso
  • Puede esperar esta función en TypeScript 3.7
  • Actualizaremos aquí cuando esté disponible en una compilación nocturna.
  • Aplazamiento de la llamada opcional hasta que se aclare su semántica en el comité

Preguntas abiertas

  • ¿Qué carcasa especial, si la hay, debería tener document.all ?

C# y otros lenguajes tienen azúcar de sintaxis para acceder a cadenas de propiedades donde null (o en nuestro caso, undefined ) se pueden encontrar en cualquier punto de la jerarquía 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'

Se necesita una propuesta sobre qué es exactamente lo que debemos codificar, teniendo en cuenta los efectos secundarios de los accesores.


Editado por @DanielRosenwasser 27 de febrero de 2018: esta propuesta también se denomina operador de "propagación nula".

Committed ES Next Suggestion Update Docs on Next Release

Comentario más útil

El encadenamiento opcional es la etapa 3

Desbloquear brevemente este solo con fines de celebración

Todos 205 comentarios

Entonces, en el primer ejemplo, podríamos emitirlo de la siguiente manera:

x && xy && xyz && xyzfoo

Pero entonces tendríamos que hacer que x, y, z y foo se evalúen como máximo una vez.

Tampoco puedes hacer && en muchos casos porque la veracidad se convierte en un problema para los primitivos.

Por ejemplo:

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

da "" .

Por lo tanto, debe hacer algunas comparaciones explícitas con null usando == para el caso general, a menos que aprovechemos el sistema de tipos (lo cual sería genial vernos hacer).

Posiblemente podríamos emitir una función de enlace monádico (no muy agradable para la salida de JS), o usar alguna transformación en operadores ternarios (más cerca del equivalente típico de JS). Claramente estoy un poco sesgado hacia lo segundo.

:+1:

Lo ideal sería que ES7 (o ES8 o ES9 o ...) implementara esto primero, ya que probablemente habría algún desacuerdo sobre la semántica exacta sobre si usar o no 0 / "" como primitivas falsas para los propósitos de cualquier operador aquí.

:+1: Me gustaría ver que TypeScript obtenga esto primero sin tener que esperar a ESxx.

El hecho de que operadores de seguridad nula simples e increíblemente útiles como "?". y "?:" NO ESTÁN en la especificación ES6 significa que las personas que elaboran la especificación ES6 deberían estar avergonzados. Esto es algo tan simple y obvio que no incorporarlo sería francamente una locura. Hay una razón por la que la mayoría de los idiomas modernos los admiten: son indispensables.

Me doy cuenta de que esto sería una desviación de la especificación actual (ya que la especificación actual es tan miope como para omitir esto). Pero es tan ridículamente útil que creo que esta única desviación estaría justificada. La gran mayoría (VAST) de los desarrolladores de TS no se verían afectados por cambios menores en la implementación, si esto finalmente se agrega a una especificación de ES. Los enormes beneficios que esto ofrecería valen el posible impacto futuro para una pequeña fracción de desarrolladores. Y dado el ridículamente lento proceso de especificación ES, esto ni siquiera importaría en absoluto durante varios años (como mínimo).

Estoy totalmente de acuerdo con brain428

@brian428 el problema aquí es que ese operador puede implementarse en ES7, por lo que si el mecanografiado va con una especificación que termina siendo diferente de la final de ES7, nadie estará contento.

el problema aquí es que ese operador puede implementarse en ES7, por lo que si el mecanografiado va con una especificación que termina siendo diferente de la final de ES7, nadie estará contento.

Creo que es un enfoque más positivo para que TypeScript implemente funciones que pueden _potencialmente_ (o no) convertirse en una futura versión de ES, porque será un banco de pruebas útil para influir en la dirección de ES.

Aquí hay un ejemplo de discusión de ES influenciada por TypeScript :

La opción TypeScript... para declarar e inicializar a través de un prefijo privado en uno de los parámetros del constructor sería útil para muchos desarrolladores.

Además, ciertamente es posible que ES adopte una función que ya está presente en TypeScript, pero con una semántica diferente (por ejemplo, sobre cómo funcionan los módulos).

ciertamente es posible que ES adopte una función que ya está presente en TypeScript, pero con una semántica diferente

Debo señalar que, en términos generales, consideramos que este es el peor de los casos. Realmente queríamos que los módulos en ES6 estuvieran finalizados antes de declarar TypeScript 1.0, pero los retrasos en el cronograma del comité lo impidieron. Esto es algo que debe evitarse, no repetirse. Realmente nos gustaría encontrar características que tengan un ~0 % de posibilidades de llegar a ES7+ (p. ej., escriba anotaciones) o que tengan un ~100 % de posibilidades de llegar con una semántica fácil de definir (p. ej., donde la flecha gruesa era dos hace años que). Es probable que los nuevos operadores caigan en el incómodo medio.

En el peor de los casos, si ES7 difiere, ¿podría un indicador del compilador admitir la implementación heredada de TS, ofreciendo así un período de gracia? Esto, junto con una documentación de migración clara, debería ofrecer a los desarrolladores una ruta directa hacia cualquier nuevo estándar.

En última instancia, el uso de cualquier característica de este tipo, aunque increíblemente útil, no es esencial para los desarrolladores. TS debería dejar muy claras las posibles implicaciones futuras de su uso desde el primer día. Si no le gusta la idea de un posible refactor administrado, no lo use. ¿Quizás una bandera del compilador opt-in para hacer cumplir este mensaje?

TS no debería volverse loco por querer influir en ES, pero en pequeños casos aislados como este, sería una pena que TS se alejara por completo.

Tal vez podríamos armar una propuesta testaferro para esto y luego tener una implementación de referencia detrás de una bandera --harmony (o algo así). De esa manera podemos impulsar el desarrollo de ES7 de esta función.

Para evitar efectos secundarios debido a búsquedas repetidas, el compilador tendrá que generar variables temporales:

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

o usa una membrana de memorización basada en Proxy .

Desde un punto de vista categórico, esta es solo la mónada tal vez aplicada a la búsqueda de propiedades, por lo que es una característica muy natural para un lenguaje en el que todas las búsquedas de propiedades pueden dar resultados indefinidos. Me sorprendería si ES7 adoptara alguna semántica distinta a la descrita en el código anterior.

El tema del códeplex tuvo bastantes votos (61)

Realmente necesito esto para aliviar el dolor de usar atom para atom-typescript .

Es muy idiomático en el código coffescript (aunque me gustaría que no fuera tan popular ya que el determinismo es mejor que un ? tonto). Abra cualquier archivo de coffescript, especialmente uno que funcione con el DOM directamente como un lápiz espaciador (donde las funciones pueden ejecutarse después de que se destruya la vista o antes de que se adjunte la vista) y encontrará miles ? usos. por ejemplo, este archivo tiene 16 https://github.com/atom-community/autocomplete-plus/blob/f17659ad4fecbd69855dfaf00c11856572ad26e7/lib/suggestion-list-element.coffee

Nuevamente, no me gusta que necesite esto, pero es el estado de JavaScript, y prefiero ? que un millón if( && fest ) { then }

Pero realmente lo necesito para mantener mi código legible. También es muy común _necesitar_ esto cuando está esperando que se complete un XHR y angular ejecuta su ciclo de resumen.

Bien, ahora he leído el hilo y veo por qué estamos esperando. Te entiendo _suspiro_.

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

coffeescript hace algunas optimizaciones, por ejemplo , almacena resultados de acceso intermedio :

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

(Creo firmemente que la comprobación undefined no es necesaria para mecanografiado: ya que deberíamos tener un tipo de inicio var verificado por mecanografiado)

+1

En las noticias de hoy, Dart está recibiendo soporte oficial: https://github.com/gbracha/nullAwareOperators/blob/master/proposal.md

Característica muy importante.

Posiblemente sea una idea extravagante, pero la generación de códigos para esta función se podría hacer muy fácilmente sin efectos secundarios si todos decidieran que estaría bien manejar la función con acceso de propiedad con clave:

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

Podría compilar a

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

Se tendría que emitir una función __chain similar a __extends . La función __chain podría simplemente iterar a través de la matriz arguments , devolviendo null cuando el próximo miembro no sea instanceof Object o no contenga el nombre del miembro. Las llamadas a funciones se pueden manejar pasando una matriz como parámetro (y usando .apply() debajo de las sábanas), así que...

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

Podría compilar a

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

Esto también mantendría el JS generado razonablemente idiomático, incluso para cadenas largas.

Necesita refinamiento obviamente... pero tal vez hay algo aquí?

Si aaa?.bbb?.ccc? devuelve el valor de a.b.c si existen todos los accesorios y resulta que es una función, entonces no podría

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

compilar para

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

?

@metaweta : Su solución solo busca void 0 ... ¿eso no anula el propósito de la característica?

¿Qué pasa con esto?

var result = one?.two?.three;

Genera:

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

Estoy bastante seguro de que esto maneja todos los casos. Las llamadas a funciones probablemente necesiten una verificación instanceof Function .

(Desventaja menor aquí de la emisión de variables locales inesperadas... tal vez podría estar envuelto en un IIFE)

@kevinb7 : ¿Qué sucede si ccc no es una función? Con el código que ha descrito, __chain siempre tendría que devolver una función válida, o se emitiría un TypeError.

@Back-io Cuando una propiedad está ausente, una búsqueda devuelve undefined === void 0. Su solución falla al buscar propiedades de valores falsos como una cadena vacía y cero.

@metaweta : No, no lo hace: http://jsfiddle.net/25LppbL6/

Además, me imagino que el equipo de TS no está loco por usar la igualdad flexible debido al hecho de que los linters de algunas personas advierten en contra.

@Back-io Sí, lo hace: http://jsfiddle.net/25LppbL6/2/

@Back-io Con respecto a nulo, no estoy seguro de cuál es la semántica prevista de a? .b. Si es "Si la propiedad b está definida, úsela", entonces mi código es casi correcto. La única forma en que obtendría un valor nulo es si se asigna como nulo, porque las búsquedas de propiedades inexistentes devuelven undefined. No detecta el caso en el que la propiedad existe pero está configurada como indefinida. Para ser completamente correcto, verificaría con Object.hasOwnProperty() en lugar de comparar con void 0 === indefinido.

Si la semántica es "si la propiedad b es verdadera, úsela", su código está bien y, en cierta medida, coincide con el idioma JS.

Umm... a menos que me esté perdiendo algo... los cambios que hiciste en el violín solo me dan la razón... var result sigue siendo undefined en los 3 casos. Y no estoy seguro de qué caso está tratando de presentar al extender los prototipos primitivos...

Estoy bastante seguro de que el comportamiento de la función sería un cortocircuito con void 0 en lugar de generar un error. (Me gusta su idea anterior de que siempre debe devolver void 0 en el caso de que falte un miembro).

¿Cuál es la salida prevista de ''?.toString ?

@ kevinb7 : ¿Qué sucede si ccc no es una función? Con el código que ha descrito, __chain siempre tendría que devolver una función válida, o se emitiría un TypeError.

Buen punto.

@metaweta : me imagino que la mayoría de la gente esperaría una referencia a la función toString , así que veo cuál es su punto, se descompone un poco en el caso de que acceda a los miembros prototipo de 0, falso, o ''.

:+1:
Angular 2 agregó Elvis Operator a su sintaxis de plantilla

@metaweta :

¿Cuál es la salida prevista de ''?.toString?

Si quisieras decir ''?.toString() sería:

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

Muestra

Tal vez podríamos armar una propuesta testaferro para esto

@kevinb7 ya existe: http://wiki.ecmascript.org/doku.php?id=strawman:existencial_operator

Hice algunas pruebas rápidas para implementar esto además de PropertyAccessExpression como su caso especial, pero no funcionó bien porque realmente necesitamos que ?. sea asociativo por la derecha (en lugar de asociativo por la izquierda como . ) de lo contrario, el emisor se vuelve innecesariamente complejo.

Hay un comentario de BrendenEich aquí que también refleja esto.

Hice algunas pruebas rápidas para implementar esto además de PropertyAccessExpression como su caso especial, pero no funcionó bien, ya que realmente lo necesitamos. ser asociativo por la derecha (en lugar de asociativo por la izquierda como .) de lo contrario, el emisor se vuelve innecesariamente complejo.

@basarat , ¿puede dar más detalles sobre eso? Un ejemplo de la diferencia entre asociativo por la derecha y asociativo por la izquierda ?. me sería muy útil.

@zlumer

¿Un ejemplo de la diferencia entre asociativo por la derecha y asociativo por la izquierda?. me seria de mucha ayuda

. se deja asociativo por lo que la expresión foo?.bar?.baz se convierte en AST (si tratamos a ?. igual):

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

La emisión de JavaScript necesaria es

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

Simplemente es más fácil hacer esta emisión (especialmente _recursivamente_) si tuviéramos lo siguiente en el AST:

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

Solo piense en _cómo convertiría el primer AST a JavaScript_ y las complejidades serían más claras. Espero que esto ayude :rosa:

@zlumer , para agregar a lo que dijo @basarat , solo pondré el ejemplo 1 + 2 + 3 .

Al analizar, tenemos que decidir cuál de estas operaciones ocurrirá primero.

Si + es asociativo a la izquierda, se interpretará como ((1 + 2) + 3) .

Si + es asociativo por la derecha, se interpretará como (1 + (2 + 3)) .

Podría preguntarse si esto realmente marcaría la diferencia. ¡En JavaScript lo haría! Considere el ejemplo "hello" + 2 + 3 .

  • Asociativo a la izquierda: (("hello" + 2) + 3) => ("hello2" + 3) => "hello23"
  • Asociativo por la derecha: ("hello" + (2 + 3)) => ("hello" + 5) => "hello5"

(Para que conste, JavaScript/TypeScript usa asociatividad izquierda para el operador + ).

@basarat , entiendo lo que dijo @BrendanEich (y él puede corregirme si me equivoco, ¡perdón por el ping!) No es que ?. sea asociativo por la derecha, sino que la propiedad de casos especiales de CoffeeScript accede en el derecho de ?. a ser asociativo por la derecha. Por ejemplo, analizará

o.p?.q.r.s

como

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

en lugar de

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

porque es más fácil de emitir.

Nuestro AST debe prestarse bien a un análisis semántico sensato, por lo que nuestro procedimiento de emisión puede darse el lujo de ser un poco más complejo para satisfacer esta necesidad.

@basarat @DanielRosenwasser gracias por las explicaciones. Hasta ahora lo entiendo, pero todavía no estoy seguro de una cosa.
El ?. asociativo por la izquierda es bastante obvio y esperado:

foo?.bar?.baz

Se convierte (aprox.):

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

Pero no entiendo en absoluto cómo funcionaría un ?. asociativo por la derecha. ¿Puede proporcionar un ejemplo?

Pero no entiendo para nada ¿cómo sería una asociativa por la derecha?. trabajo. ¿Puede proporcionar un ejemplo?

@zlumer El comportamiento del tiempo de ejecución se dejará asociativo. Justo estaba hablando del AST como DanielRosenwasser también aclaró: is not that ?. is right-associative, but that CoffeeScript special cases property accesses on the right of the ?. to be right-associative .

Nuestro AST debe prestarse bien a un análisis semántico sensato, por lo que nuestro procedimiento de emisión puede darse el lujo de ser un poco más complejo para satisfacer esta necesidad.

@DanielRosenwasser gracias por los comentarios :rose:

@basarat gracias, de repente todo quedó claro :smiley:

Similar al comentario de febrero de @basarat , yo.... _suspiro_...

Sin embargo, si lo piensa bien, el 99 % de los casos de uso de esto serán para verificar un puntero nulo a un objeto. Hablando con franqueza, ¿quién hace x?.b?.c cuando x es un number ? Simplemente no hay muchos casos de uso en la vida real para cadenas _largas_ cuando _no_ estamos hablando de un objeto (con la posible excepción de string ). Para cadenas cortas, creo que podemos vivir con x && x.b o x === 0 ? null : x.b .

Entonces, ¿podemos decir que ?. solo funciona en tipos de objetos? Cualquier otro tipo arroja un error de sintaxis. Y no permitir llamadas a funciones dentro de la cadena.

Luego todo se transcribe a a && a.b && a.b.c .

@schungx ¿Qué sucede si un miembro de la cadena es del tipo "cualquiera"? ¿Desautorizar por completo? ¿O simplemente dejarlo pasar y esperar lo mejor?

Bueno, ¿mi sugerencia? Desautorizar por completo. Poco elegante como el infierno, lo sé... :-)

Pero mi razón:

  1. Esta es una abreviatura, por lo que si alguien está usando any , solo use la abreviatura.
  2. Si alguien está usando TypeScript, lo más probable es que lo esté usando para el soporte de escritura, ¡así que espero que no tenga muchos any alrededor!
  3. any realmente debe manejarse con cuidado. Permitir el uso de tales abreviaturas con un tipo flexible como any es realmente pedir que ocurran errores. En mi opinión, any debería ser lo más limitado posible. Es algo así como los (void *) de C: ¡el hecho de que te entreguen una bomba nuclear no significa que debas activarla solo porque puedes!

¡Este sería un operador increíble! Especialmente por 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

Sin embargo, propongo una más clara, ¿para no confundir? de la a? b : c sentencias con a?.b sentencias:

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 la notación de corchetes, recomiendo dos puntos en lugar de uno solo, ya que es consistente con los demás cuando no se usan corchetes. Por lo tanto, solo el nombre de la propiedad es estático o dinámico mediante corchetes.

Dos puntos, significa que si es nulo o indefinido, deja de procesar y asume que el resultado de la expresión es nulo o indefinido. (ya que d sería nulo o indefinido).

Dos puntos lo hacen más claro, más visible y más espacial para que entiendas lo que está pasando.

Esto tampoco está jugando con los números, ya que no es el mismo caso, por ejemplo.

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

Acerca de los números, dos opciones: ¡usted llama cuál puede adoptarse, pero recomiendo la primera por compatibilidad con las reglas existentes!

  1. Debería fallar como lo hace ahora ( 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. Debería funcionar ya que entiende que no es un número que llama a una propiedad desde 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

Con nombres dinámicos:

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

¿Qué piensan ustedes, amigos?

La sintaxis PS foo?.bar y foo?['bar'] también funcionaría.

Sin embargo, el uso del operador ? : actual y ?. puede ser muy confuso en la misma línea.

por ejemplo, usando ?. y ?['prop']

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

a diferencia de los puntos dobles .. y ..['prop']

var a = { x: { y: 1 } };
var b = condition ? a..x..y : a..y..z;
var c = condition ? a..['x']..['y'] : a..['y']..['z'];
¿Cuál te parece más claro?

Muy interesante. :+1:

En mi humilde opinión, dos puntos serán más confusos. Hay idiomas en los que dos puntos representan un rango (p. ej 1..4 ) y TypeScript puede agregar esta función en el futuro.

Un signo de interrogación también tiene el significado semántico de incertidumbre o condicional y dos puntos no transmiten el mismo significado.

@schungx Está bien , pero ayudaría para las posibilidades extrañas como esta: a?['b'] o este a?() .

+1 ?

a?['b'] y a?() se comportan bien en coffeescript, y me parecen buenos.

Pero, de nuevo, podría ser ciego al guión del café.

+1 Ruby acaba de implementar el operador existencial https://twitter.com/mikepack_/status/657229703443451904. Typescript también necesita esto, independientemente de la sintaxis específica.

Esto sería hacer un código más limpio 👍

+1 ¡Necesito esto!

Por favor, impléntate?. operador, como lo hace C#

+1 Hace mucho que me perdí de esto

+1
tan feo de escribir: algunaVariable && algunaVariable.algúnMiembro
cuando podrías escribir: algunaVariable?.algúnMiembro

+1, esto sería genial... (función más buscada para mí)

+1, es una buena idea!
Solo que, si el objeto es complicado, la expresión estará llena de ?. después de cada propiedad (var result=myValue?.a?.b?.c?.d?.e;) cuando necesito recibir un valor de la última (var result=?myValue.abcde;).

+1: podría decirse que esta es una de las mejores funciones de CoffeeScript y es, con mucho, la función TypeScript más deseada de mi equipo después de convertir gran parte de nuestro código de CS a TS.

+1 sin embargo, esto es demasiado complejo:

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

Me gusta esto:

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

El tipo x?.y?.z siempre es el tipo del campo z . Por supuesto, el tipo debe ser anulable y el valor real puede ser nulo. Si no es nulo, debe ser del tipo del campo z .

+1 Esto iría bien con la visión de Typescript de facilitar el desarrollo de proyectos JS complejos a gran escala.

¿Alguna actualización sobre esto? ¿Es este un caso en el que la comunidad vota por esta función para que se considere? ¿O se ha considerado pero hay algunos desafíos de ingeniería?

No hay actualizaciones hasta ahora porque la introducción de una nueva sintaxis a nivel de expresión es peligrosa sin algún tipo de propuesta del comité de ECMAScript.

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

Aquí hay una discusión más actual sobre los operadores existenciales (muchos pensamientos, pero no parece mucha acción):

¿Dónde debo escribir "+1" para ayudar a traerlo a ES?

@msklvsk ESDiscutir

Cerrando esto por ahora. Dado que en realidad no hay nada específico de TypeScript que requiera esto a nivel de expresión, este tipo de gran cambio de operador debería ocurrir en el comité de especificaciones de ES en lugar de aquí.

Los cables trampa generales para reevaluar esto serían una propuesta concreta de ES que llegue a la siguiente etapa, o un consenso general del comité de ES de que esta característica no sucederá por mucho tiempo (para que podamos definir nuestra propia semántica y ser razonablemente seguro de que "ganarían").

la vida apesta

Eliminación independiente :+1:s. Utilice la función de reacciones de GitHub o envíe flores y dulces a su representante de TC39 más cercano.

Una vez que me acostumbré en coffeescript y Swift, no hay vuelta atrás. TS está muerto para mí tal como está actualmente.

@algesten Puedo estar de acuerdo con swift si consideramos el lenguaje en sí, no estoy seguro del soporte a largo plazo coffescript (uso CoffeScript en proyectos de producción). Podemos estar de acuerdo en estar seguros de las tendencias de análisis de lenguaje a partir de junio de 2016 como esta , donde podemos ver claramente lo que está pasando con coffescript recientemente, mientras que TypeScript ha experimentado el crecimiento más rápido en los últimos años:

TypeScript: fuera de Go o Swift, el lenguaje de más rápido crecimiento que hemos observado en los últimos años es TypeScript. El superconjunto de JavaScript respaldado por Microsoft y la base Angular 2 han logrado avances significativos por segundo trimestre consecutivo, saltando del puesto 31 al 26. Ese fue el cambio individual más grande en cualquiera de los 30 idiomas principales y el segundo salto más grande en general (ML estándar, 7 lugares). De hecho, en el puesto 26, TypeScript ahora está empatado con Erlang, un lugar detrás de Powershell y cuatro detrás de CoffeeScript, que está justo fuera del Top 20. La pregunta que enfrenta el lenguaje no es si puede crecer, sino si tiene el impulso. para romper el Top 20 en los próximos dos o tres trimestres, superando a CoffeeScript y Lua en el proceso.

Hasta 2014, las tendencias coffescript fueron más que positivas, como puede ver aquí , fue entonces cuando comenzó el declive.

Con la nueva verificación nula (indefinida) estricta de TypeScript 2.0, es imprescindible, porque de lo contrario no puede usar el encadenamiento de funciones.

Esto debería estar en la próxima versión de ECMAScript. Teniendo en cuenta que esto sería aún más útil en JavaScript estándar que TypeScript y considerando que la implementación sería naturalmente una verificación de indefinido o nulo en lugar de una verificación veraz, debería ser trivial para TC39 agregar.

La implementación simple y que sería idiomática en JavaScript, sería simplemente un cortocircuito cuando se encontrara con un valor nulo o indefinido, y devolvería undefined.

@bterlson , ¿esto tiene sentido? ¿Cuáles son las probabilidades de que se acepte tal propuesta?

Uno de los grandes beneficios del mecanografiado es que "el futuro te lo dan hoy". Esta es una de las características que me sorprendió que aún no están allí.

Espero que se agregue pronto, ya que el encadenamiento de funciones es un idioma popular y la verificación estricta de nulos ya no funcionará a menos que el ?. se agrega el operador.

¡La maravillosa verificación nula de TS 2 se convirtió en un buen tener que tener!

Al leer este hilo, estoy un poco sorprendido de cómo esto no recibe más atención de los mantenedores.

En un mundo ideal, TypeScript debería liderar el camino para ES y no al revés. En serio, ¿dónde estaría TypeScript ahora si el equipo de TS siempre hubiera esperado a que ESx propusiera y finalizara una función o una sintaxis?

Esta sintaxis es realmente "imprescindible", como han señalado otros. Incluso recibió algunas buenas propuestas en este hilo hasta ahora. Creo que la implementación debería coincidir con estas expectativas.

  • En general, una expresión a?.b debe ser válida en tiempo de compilación si y solo si a.b es válido.
  • Debe evaluar cada expresión en la cadena solo una vez.
  • Debería ser un cortocircuito.
  • Si la ejecución alcanza una expresión media con null o undefined , entonces ese valor debería ser el valor de retorno.

¿Cuáles cree que son las partes que pueden generar desacuerdos cuando ES lo especifique?

Para a?.b no veo ningún conflicto con la sintaxis existente (¿y futura?). Si el analizador encuentra el token ?. , puede tratarlo como un 'operador de navegación seguro' (con las expectativas descritas por @cervengoc).

Los conflictos de sintaxis surgen solo cuando se permiten a?(b) y a?[b] , ya que estos también podrían interpretarse como el comienzo de una expresión de operador ternaria ?: . Pero para empezar, creo que estos podrían dejarse de lado y admitir solo la sintaxis a?.b ya haría felices a muchos desarrolladores.

En un mundo ideal, TypeScript debería liderar el camino para ES y no al revés.

En términos de sintaxis a nivel de expresión, no estamos de acuerdo en absoluto. Hay un comité que impulsa un estándar por una razón: no para que las acciones únicas de un jugador puedan decidir unilateralmente el futuro de JavaScript.

Esta sintaxis es realmente "imprescindible", como han señalado otros.

Si esto es imprescindible para TypeScript, ¡también lo es para JavaScript! Una vez más, lleve sus inquietudes al comité de ECMAScript .

¿Cuáles cree que son las partes que pueden generar desacuerdos cuando ES lo especifique?

Como mínimo, creo que habrá desacuerdo sobre si la sintaxis hace un cortocircuito a null o undefined al encontrar esos valores, o si siempre hace un cortocircuito a undefined . También habrá cierta controversia sobre si se admite o no alguna forma de sintaxis entre paréntesis. También está la cuestión de cuál es el comportamiento de a?.b.c . También está la cuestión de ?. vs .? vs a.b? . Está la cuestión de cuál es el efecto de esto en el operador delete .

El hilo ES DISCUSS sobre esto tiene más de 100 comentarios. ¡No hay falta de ambigüedad! Es fácil mirar un ejemplo de forma aislada y pensar que no puede haber toneladas de casos de esquina. Hay. Probablemente es por eso que nadie ha defendido esto en TC39 todavía y _también_ por qué no nos apresuramos a agregar una función que tiene mucha ambigüedad sobre cómo debería comportarse.

Me disculpo, no leí el hilo mencionado, definitivamente le echaré un vistazo para ver más.

Vemos esto un poco diferente. Sobre el comité, en mi honesta opinión, esta es una de las principales razones por las que JavaScript nunca será _bueno_. Solo como ejemplo, muchos de los software más exitosos que he visto (como Total Commander o IrfanView) son exitosos porque son mantenidos y diseñados por UNA persona, y no por un *comité". Por supuesto, esto no es un ejemplo correcto Pero estoy casi seguro de que si, por ejemplo, usted solo hubiera diseñado el ES6 completo, entonces el mundo sería un lugar mejor ahora.

Además, las ambigüedades que ha mencionado son en un 99% cosas _teóricas_ y algo irrelevantes desde el punto de vista del desarrollador. ¿A quién le importaría lo que devuelve, null o undefined ? Solo elige uno, y lo usaremos así.

Considerándolo todo, usted y ese comité están en un lado diferente al de la mayoría de nosotros, y las cosas desde ese lado suelen ser más complejas de lo que realmente son. Y esto puede conducir a cierta contraproductividad, por decir lo menos. No lo tome como algo personal, pero según mi experiencia en general, sería mejor que algunas personas salieran de la sala de conferencias con más frecuencia y echaran un vistazo a algún código.

Por supuesto, no se ofenda, no se tome nada como algo personal, tengo un gran respeto por usted y por todo el equipo de TS, porque revolucionó el desarrollo del lado del cliente de muchos desarrolladores, incluyéndome a mí, y gracias por todo su trabajo. Estamos un poco decepcionados con este específico, supongo.

Un último pensamiento. Revisé el hilo ES mencionado, y una cosa es completamente segura: lo están complicando demasiado. Quieren diseñar algo que sea bueno para cada escenario.

Personalmente, estaría completamente satisfecho y feliz con un operador de acceso condicional para miembros . No necesita invocación condicional, firma de índice condicional, no necesita admitir todos los escenarios sofisticados que son código JS válido. Eso es todo. Pero en cambio, es probable que sigan sentados allí y discutan cómo hacer todo a la vez, lo cual es un gran plan, pero al final del día no tendremos nada.

Considerándolo todo, usted y ese comité están en un lado diferente al de la mayoría de nosotros, y las cosas desde ese lado suelen ser más complejas de lo que realmente son.

No creo que esté reflejando con precisión el verdadero estado de Ryan o TC39. Ryan y el equipo de TypeScript establecieron objetivos de diseño muy claros para TypeScript. Uno de los objetivos originales y aún muy actuales es que TypeScript sea un superconjunto de JavaScript. No es el idioma que a la gente le gustaría que fuera (por ejemplo, Dart, Haxe). Cuando se trata de sintaxis, el equipo de TypeScript ha aprendido de la manera más difícil el costo de inventarlo previamente (por ejemplo, módulos). Nos dirigimos de cabeza a un desafío con miembros privados de clases también, donde la sintaxis ES propuesta es totalmente incompatible con la sintaxis que usa TypeScript. ¿Por qué? Porque lo que puede parecer sencillo en la superficie es imposible de lograr debido a los desafíos de tiempo de ejecución de un idioma.

TC39 ha "guardado" JavaScript en mi opinión. ES4 fue abandonado, no porque careciera de ideas buenas e innovadoras, sino porque rompería Internet. TC39 se puso en forma, compartieron y fueron totalmente abiertos en sus discusiones y en cómo toman decisiones y nos entregaron ES2015, que es muy parecido a ES4, pero no rompió Internet. Es sorprendente que esencialmente tengamos tiempos de ejecución de JavaScript que ejecutan código de hace 10 años sin problemas, pero admiten muchas mejoras significativas en el lenguaje. ES2016 fue la calma antes de la tormenta. ES2017 tiene una cantidad "razonable" de funcionalidad y cambio y un proceso de gobierno claro que va en la dirección correcta.

Así que estar en el "lado diferente" de las cosas claramente ha funcionado, en mi opinión. Lo que supera la conveniencia de las características "imprescindibles".

@kitsonk No quise decir "lado diferente" negativamente, y especialmente no quise degradar el trabajo que se puso en TypeScript o ES6. Además, lo mejor de TypeScript IMO es que realmente tenía y tiene un objetivo de diseño claro, y está bien protegido contra convertirse en un caos como muchas otras cosas de código abierto.

Solo quería decir que esta característica es un claro ejemplo de que un grupo de personas geniales terminarán pensando demasiado y complicando demasiado las cosas, en lugar de seguir el camino fácil y simple, y aceptar algunas limitaciones como no admitir invocaciones o firmas de índice, etc. Alguien en ese foro incluso sugirió usar esta sintaxis en las tareas, lo cual es una locura. Sigo pensando que este fenómeno es contraproducente en este sentido.

Entiendo que, de su parte, es un dolor que, por ejemplo, los miembros privados se vuelvan incompatibles con el concepto final de ES6. Pero por otro lado, lo TENÍAMOS. Mucho antes de ES6. Y ese es el punto principal de nuestro lado. En términos generales, no nos importa cómo se las arregla para emitir el código apropiado, simplemente lo estamos usando felizmente. Lo mismo con los módulos, y todo. Nosotros (o al menos yo) no vimos esos dolores de lo que estás hablando, siempre estuvimos contentos con miembros privados o módulos.

Esta característica particular está en CoffeScript cuando leí sobre ella. ¿Por qué nosotros, los simples desarrolladores, siempre tenemos que hacer concesiones al elegir una plataforma/biblioteca/complemento, etc.? Quiero decir siempre . Esto es un poco molesto. Aquí tenemos un gran lenguaje, que tiene un gran potencial, que deja completamente atrás a todos los demás participantes (¡incluido ES!), y que ha revolucionado con éxito una gran parte del desarrollo del lado del cliente, y cuando se trata de esta característica "simple" ( Me refiero a la parte de acceso de miembros al menos), escuchamos que no se implementará hasta que ES se comprometa con esto.

Quería avisarles a las personas que esta característica pasó de la etapa 0 a la etapa 1 en la reunión TC39 de hoy.

Confirmación relevante: https://github.com/tc39/proposals/commit/cb447642290a55398d483f5b55fb7f973273c75d
Agenda de la reunión: https://github.com/tc39/agendas/blob/master/2017/01.md

¡Guau! ¡Eso es enorme!

También vale la pena un enlace a https://github.com/claudepache/es-opcional-chaining

Algunas "sorpresas" que veo aquí (no digo que no esté de acuerdo, solo cosas que probablemente hubiéramos hecho de manera diferente si hubiéramos hecho esto antes):

  • null no se produce a partir de una expresión a?.b : cuando a es null se produce undefined en su lugar
  • ~ Propagación en puntos encadenados: a?.b.c.d no arrojará si las propiedades b y c son undefined ~ Ryan no puede leer
  • ~Propagación en presencia de paréntesis : incluso (a?.b).c no arrojará si b no está definido~ Ryan no puede leer
  • ~La propagación incluso ocurre en las llamadas a métodos: a?.b.c().d devolverá undefined si la invocación c devuelve null ~ Ryan no puede leer
  • Se admite el operador delete
  • Se admite la sintaxis de horquillado a?.[x]
  • Se admite la sintaxis de llamada de función func?.(...args) , incluso para llamadas que no son de método (!)

Esperaría ver cambios en esas áreas entre ahora y la etapa 2.

Creo que coffeescript lo hizo bien.

a?.bc se lanza si b no está definido.

a?() y a?[0] son ​​buenos.

  • Propagación en puntos encadenados: a?.bcd no arrojará si las propiedades b y c no están definidas
  • Propagación en presencia de paréntesis: incluso (a?.b).c no arrojará si b no está definido
  • La propagación incluso ocurre en llamadas a métodos: a?.bc().d devolverá indefinido si la invocación c devuelve nulo

Esos puntos no me parecen exactos. De la propuesta:

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

Wow, lo leí totalmente mal. Estás bien. Actualizando

@algesten de la propuesta original:

a?.()

b?.[0]

dulce. el operador puede ser considerado como ?. entonces.

Hay una conversación adicional aquí: https://github.com/estree/estree/issues/146

Una cita que podría aplicarse bien: «Haz que las cosas simples sean fáciles y las difíciles, posibles ». Por lo tanto, quizás, admita bien los casos más comunes, mientras omite (al menos inicialmente) los casos complicados/raros, mientras que todavía se les permite "hacerse manualmente" con una sintaxis más larga (existente).

Solo mis dos 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: las excepciones lanzadas en captadores no deberían causar la fusión

@RyanCavanaugh , sí... No lo pensé bien.

¿Está esto en el radar para su implementación ahora, o el equipo de TS esperará a que la propuesta de ES avance más?

Está en nuestra lista corta; Todavía tenemos algunas preguntas/inquietudes, pero debería ver un movimiento al respecto en el próximo mes.

No estoy seguro de dónde provino el comentario de @mhegazy : la cantidad de preguntas abiertas sobre la propuesta TC39 es demasiado grande para que podamos hacer un trabajo significativo aquí. Específicamente, las preguntas sobre cómo interactúan null y undefined y qué sintaxis es realmente compatible deben abordarse primero. La etapa 2 es un mínimo absoluto y preferiríamos la etapa 3 dado el impacto en el comportamiento del tiempo de ejecución.

¿Este código simplemente funciona?

a == undefined ? expression : undefined

expression significa hacha, a[x], a(x), delete también podría generarse aquí

entonces a?.b?.[c]?.(d) generará

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

parece que pasara toda la regla de RyanCavanaugh


si odia el operador == , también podría ser a === undefined || a === null

@zh99998 _tienes_ que odiar == porque '' y 0 también se equiparan. Casi se argumenta que el comportamiento del tiempo de ejecución debería ser una especie de verificación de (typeof value === 'object' || typeof value === 'function' || typeof value === 'symbol') && value !== null , que ahora se está volviendo bastante complejo.

Como dijo @RyanCavanaugh , es poco probable que progrese hasta que la Propuesta TC39 progrese al menos a la etapa 2 o 3.

Veo que == solo es igual a null y undefined como
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness

y la prueba pasó en la consola de Chrome:

'' == undefined
false
0 == undefined
false

@kitsonk indefinido solo coaccionar a nulo y viceversa. Ningún otro valor obliga a undefined o null.

Pareces estar confundido con valores falsos. 0 y "" son de hecho falsos, pero nunca forzarán a nulo ni indefinido.

== implica coerción. null == 0 es falso porque nada excepto indefinido puede coaccionar a nulo. Lo mismo ocurre con undefined == 0 .

Otro ejemplo podría 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

Te dan la imagen.

Veo muchos intentos de usar operadores ternarios, pero cualquier implementación que haga referencia a la misma propiedad varias veces podría tener efectos secundarios inesperados.

Afortunadamente, JavaScript tiene IIFE, por lo que puede almacenar el resultado de un elemento de acceso en un parámetro de función, consultarlo tanto como desee y nunca evaluarlo más de una vez. En mi ejemplo a continuación, creo una lambda coalesce que se puede llamar varias veces en lugar de una expresión que contiene operadores ?. .

Una cosa de la que los implementadores del lenguaje deberían preocuparse son las llamadas a matrices y funciones en medio de una expresión. Creo que el compilador podría manejar la detección de esas situaciones y simplemente agregar esas expresiones al final de la llamada 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]

Tenga en cuenta que no hay necesidad de un global real llamado "coalesce". Este código podría insertarse directamente en cualquier expresión. Sin embargo, podría reducir la hinchazón dándole un nombre.

No me preocupa demasiado la sintaxis utilizada por el operador o si los implementadores del lenguaje quieren esperar un estándar. Solo pensé en mostrar otro enfoque.

Esto también trae a colación la necesidad de un operador ?? . En C#, esto reemplaza cualquier expresión null con lo que esté a la derecha.

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";

Desafortunadamente, la veracidad captura demasiados casos extremos ( 0 , false , etc.). Al trabajar con un operador de fusión nula ( ?. ), querrá 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 , en su ejemplo con coalesce, tendría que haber una manera de evitar que ocurran llamadas a funciones y acceso a miembros si la verificación anterior devuelve nulo. En el ejemplo de x?.f(), no se debe llamar a f si x es nulo.

@bschlenk No creo que esté de acuerdo. Creo que debería fallar con un mensaje como null is not a function , pero eso no depende de mí, supongo.

La reciente oleada de comentarios que detallan posibles formas de implementar el operador es un poco extraña.
La implementación es probablemente un problema resuelto.

Si está interesado, existe la biblioteca idx , que se parece mucho al comportamiento del operador ?. , aunque también ignora muchos detalles del operador planificado. De todos modos, sus especificaciones para la salida de compilación pueden ser de interés para cualquiera que se pregunte cómo se podrían implementar estas cosas.

TS podría generar algo así o podría generar algo completamente diferente, pero no creo que eso sea lo que estamos esperando aquí. Aquí se ha dicho muchas veces que TS no conseguirá el operador hasta que la propuesta de ES se mueva en una dirección u otra.

Es la semántica la que todavía tiene algunas incógnitas, enumeradas aquí y aquí .

Tenga la seguridad de que implementaremos esto correctamente y no necesitamos otros 100 comentarios aquí para averiguar qué hacen || y ? ... : ... . Como señaló @noppa , solo estamos esperando que finalice la especificación ES.

https://github.com/babel/babel/pull/5813 (que acompaña a babylon PR ) acaba de fusionarse con preset-stage-1 de Babel. La especificación aún se encuentra en la etapa 1, por supuesto, pero esto ayudará a avanzar.

Tal vez me equivoque, pero no vi ningún enlace obvio a la propuesta tc39 en este hilo, así que aquí está para los futuros lectores: https://github.com/tc39/proposal-opcional-chaining

Para su información, el encadenamiento opcional estará en TC39 la próxima semana https://github.com/tc39/agendas/blob/master/2017/07.md

@jehugaleahsa Creo que tienes razón y estaré presentando ?? en TC39 la próxima semana. Puede seguir la propuesta aquí: https://github.com/gisenberg/proposal-nullary-coalescing

Veo que el encadenamiento opcional se trató en el TC39... ¿Cuál fue el veredicto?
Esperemos que haya sido suficiente para hacer avanzar esto en Typescript 😉

@markwhitfeld Del resumen de notas :
Operadores de encadenamiento opcionales: permanece en la Etapa 1, regresará más tarde con definiciones más claras para varias opciones y respuestas a los comentarios.

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

Todavía hay preguntas abiertas sobre cómo debe comportarse el operador, por lo que parece que aún no está en un estado en el que se pueda agregar a TypeScript.

A partir de esas notas, parece que el comité no tiene idea de cómo funcionan las opciones presentadas, habría pensado que habrían tardado en conocer las propuestas que planeaban discutir, antes de discutirlas. Supongo que pasará un tiempo más hasta que pueda habilitar controles nulos estrictos.

Espero mucho que opten por la opción 2/4 (que es el estado actual de la propuesta de todos modos).

15 de julio de 2014 - 4 de septiembre de 2017, nada aún

@frankfvb claramente no ha leído el problema.

Ha habido mucha discusión que ha llevado al equipo central a creer que sería imprudente implementar en este momento hasta que haya más progreso en la propuesta de ECMAScript que afectaría directamente la funcionalidad de esta función en TypeScript.

A partir de la última reunión del comité de estándares de ECMAScript, la propuesta permanece en la Etapa 1 ya que tiene algunas preguntas muy fundamentales sobre cómo se implementaría. Si bien no es una regla estricta y rápida, TypeScript solo implementará las propuestas de la Etapa 3. A veces implementa las propuestas de la Etapa 2 si cree que es de importancia crítica y que el uso potencial en TypeScript impulsa la evolución del estándar.

No estoy seguro de cómo las personas más claras pueden ser al respecto.

Como dije antes , esto está en nuestra lista corta. estamos esperando que TC39 llegue a algún tipo de consenso sobre la semántica del operador. odiaríamos apagarlo y luego interrumpir a los usuarios.

Este no es el hilo para repetir la discusión TC39

Si quieres opinar en la propuesta, ve a comentarlo en el lugar correspondiente . Yo también tengo opiniones, pero dejarlas en este hilo no va a hacer nada.

Creé algo simple que satisface mis necesidades actuales. Solo funcionará en una cadena donde cada enlace sea un nombre de propiedad, por lo que no se admite el acceso a un elemento en una matriz (por ejemplo).

Implementando un Operador Elvis realmente simple en TypeScript

Además, si tiene lodash/guion bajo, ya puede usar _.get(Book, 'author.name.firstName') que hará lo que esto quiere

Editar: Aparentemente, este es un mal consejo debido a problemas de tipo con el método _.get() . Ver comentario a continuación

@tolgaek , _.get tiene malas tipificaciones, incluso con estas mejores tipificaciones ( aún no fusionadas , debido a los autores) el mecanografiado definitivamente puede deducir el tipo de resultado solo si la profundidad del objeto es 1, en todos los demás casos es any y debe verificarse en tiempo de ejecución

Por otro lado, con el operador elvis mecanografiado puede inferir el tipo de resultado en objetos con cualquier profundidad, es por eso que espero con ansias el operador elvis

Oh, ya veo, no sabía que había un problema de tipeo. Gracias @BjornMelgaard

@mhegazy , ¿no se puede implementar primero esta característica y marcarla como característica experimental? Creo que las personas no deberían tener problemas si se modifican las especificaciones en la función experimental.

A Elvis no le hace gracia esperar tanto tiempo.

Ha aterrizado en Babel7. Texto mecanografiado, vamos lento... Alguien, por favor, haga que esto suceda.
:)

@gs-akhan el complemento de Babel implementa una versión antigua de la propuesta de hace unos meses. Ha habido cambios en la propuesta desde entonces (incluido un cambio significativo en la forma en que se analiza el operador), y es probable que haya más cambios antes de que la función llegue a la etapa 2 (y mucho menos a la etapa 3), por lo que cualquier código escrito con el babel actual El complemento podría romperse cuando se publique la función real. Babel implementa intencionalmente las funciones propuestas antes de que sean estables para que los autores de especificaciones y otras partes interesadas puedan probar la función tal como se propone. El hecho de que Babel haya implementado una función no significa que pueda implementarse de una manera que no requiera cambios importantes en el futuro.

@alangpierce Eso tiene sentido. Gracias

Entiendo que este es un operador muy, muy bueno, pero tenerlo disponible antes de que se hayan resuelto sus reglas es una jugada y no lo vamos a hacer. El comportamiento de tiempo de ejecución del operador todavía está cambiando; si escribe código hoy, podría dejar de funcionar mañana de maneras que no son evidentes de inmediato: tal vez fallas raras, tal vez corrupción de datos, ¿quién sabe? Un poco de paciencia ahora le ahorrará mucho dolor unos meses más adelante.

Empezando a pensar que este ticket debería bloquearse (no cerrarse) hasta que la especificación esté lista.

¿Cuándo estará lista la especificación?

@oliverjanik El borrador de especificación actual se puede encontrar aquí . Hay un elemento de la agenda para avanzar la propuesta a la etapa 2 en la próxima reunión del TC39 (26-9/28). Estaré presentando estas diapositivas en ese momento. Para las personas que deseen realizar una revisión anticipada y proporcionar comentarios, presenten los problemas en el repositorio de propuestas .

¡Muchas gracias @gisenberg por defender este problema por nosotros! Estaba pensando en armar un conjunto de diapositivas de resumen para ayudar a aclarar las opciones en torno al operador que podrían usarse en la reunión TC39 para evitar confusiones, pero ya lo han hecho. ¡Impresionante trabajo!
Tal vez otra cosa que podría ser beneficiosa para la conversación TC39 (y la plataforma de diapositivas) es observar la semántica y la sintaxis del operador en otros idiomas. Aunque otros lenguajes no deberían dictar necesariamente cómo debería funcionar en Javascript, sería útil mantener el operador similar al de otros lenguajes para evitar confusiones.
Suerte para la próxima semana!!!

Perdón por desviarme un poco del tema una vez más, pero pensé que algunas personas aquí podrían encontrar interesante que en Flow ahora es posible agregar definiciones de tipos que funcionen un poco para funciones de obtención seguras como _.get .

Ejemplo: flowtype.org/try

No es la pieza de código más bonita que existe y no distingue correctamente entre nulo e indefinido, pero aparte de eso, parece funcionar bastante bien.

AFAIK, lo único que le falta a TS para que haga lo mismo es algo así como $NonMaybeType .
No es que eliminaría la necesidad de este operador, por supuesto, solo pensé que era genial.

Esto no llegó a la Etapa 2 en la última reunión de TC39 debido a preocupaciones sobre la consistencia sintáctica con respecto al acceso de corchete vs punto vs llamada y comportamiento semántico (previsibilidad en torno a los efectos secundarios de las expresiones a la derecha de un undefined / null -operando evaluado), así como preguntas abiertas sobre qué tipo de expresiones se permiten sin opción (por ejemplo x.?b() cuando x.b es un number )

(vota 🎉 en este comentario para tirar fruta podrida)

Gracias por dejarnos saber. ¡Qué fastidio! ¿Tal vez el alcance deba reducirse para que sea más simple pero aún útil?

¿Tal vez el alcance deba reducirse para que sea más simple pero aún útil?

Este es el desafío al que se enfrenta TC39, que, aunque apesta, debo admitir que me alegra que la gente esté pasando por esto. Es realmente difícil para ellos introducir una sintaxis de nivel bastante compleja y, de hecho, en este caso, la escritura débil del lenguaje en realidad está causando una cantidad significativa de casos extremos que deben abordarse, o se obtiene un código que funciona 💥 que es no es bueno para nadie. No creo que si introduce un operador como este, pueda reducir su alcance. Sería más fácil para los implementadores cubrir el 90 % de los casos, pero no creo que ¯\_(ツ)_/¯ sea un código válido para el 10 % restante.

Chicos, más de 3 años después, diría que es hora de decir "vete a la mierda" al comité y hacerlo a nuestra manera, lo que sea idiomático para TypeScript. De todos modos, esta función no puede funcionar correctamente sin escritura estática.

Realice una implementación de TypeScript con una sintaxis que sea explícitamente incompatible con la propuesta TC39. Una vez que ES obtenga un operador de navegación segura, TypeScript tendrá dos opciones: una con semántica de mierda que es compatible con ES y otra que está vinculada al sistema de tipos. Esto significa que no puede usar el operador de navegación segura de TS con ningún "tipo", y eso está bien.

@notsnotso No se supone que Typescript rompa JS, para eso está Coffeescript.

Tal vez una buena solución es:

  • implementar muy simple? operador para desarrolladores impacientes, como característica experimental (como decorador), con advertencia: romperá su código en el futuro
  • espere otros 3 años cuando se escriba el estándar, impleméntelo como una característica no experimental. Escribir tutorial, lo que podría estar roto. Incluso, con escritura estática, es posible escribir advertencias cuando las personas compilarán código con la implementación de un nuevo operador.
    "Hazlo a nuestra manera, lo que sea idiomático para TypeScript" no es un caso, porque en 3 años la gente se enfrentará al problema de que ts elvis no funciona de la misma manera que en js.

Sin embargo, no me sorprende que esto no haya sucedido, para ser honesto. en mi original
publicación, señalé la ambigüedad con respecto a la indexación, las llamadas a funciones, etc.
Honestamente, cuanto más pienso en ?. , más siento que no
pertenecen a JavaScript, ya que tiene undefined .

Preferiría una utilidad de propósito más general que resolviera este problema
y más. Un pensamiento que tuve fue algo así como try/catch línea
expresión ( let x = try a.b.c else 0 ) en conjunción con un operador que
verificado por "nuly" (p. ej., x ?? 1) en lugar de "falsey" (p. ej., x || 1).
Entonces, los combinarías así: try a.b.c ?? 0 else 0 . Es prolijo, sí,
pero básicamente dice: Intenta evaluar abc y si el resultado es null o
undefined , devuelve 0. Si a o b es undefined y se lanza una excepción,
atraparlo y devolver un 0.

Una alternativa sería hacer que la cláusula else sea opcional, por defecto
undefined . Entonces podrías escribir la expresión como: let x= (try a.b.c) ?? 0 . Eso es bastante compacto, evita la ambigüedad y proporciona un aspecto más
solución de propósito general que puede resolver otros problemas.

No estoy diciendo que debamos impulsar esto en su lugar; todo lo que digo está ahí
son alternativas a ?. y realmente deberíamos explorar nuestras opciones hasta que
encuentre uno que encaje bien con el lenguaje JavaScript.

El jueves 5 de octubre de 2017 a las 7:51 a. m., Dmitry Radkovskiy [email protected]
escribió:

@notsnotso No se supone que Typescript rompa JS, eso es lo que
Coffeescript es para.


Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/Microsoft/TypeScript/issues/16#issuecomment-334441781 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABTgPilbZfuKc2egdBrYfdTHHeDl3F6Sks5spMLLgaJpZM4CNapf
.

@zlumer buen chiste. Dicho esto, una característica específica de TS no rompería Javascript.

@jehugaleahsa Me gusta tu propuesta, y se encuentra en otros idiomas. Creo que funcionaría bien para TS.

Diré que realmente no entiendo la gran necesidad de esperar hasta que ECMAScript acepte el operador. TypeScript agregó clases, módulos, lambdas, todo mucho antes de que llegaran a ECMAScript. De hecho, uno de los objetivos declarados de TypeScript era/está probando características experimentales de JS. TypeScript proporcionando su propia implementación sin duda ayudaría a informar estas discusiones en el comité.

Ustedes han estado dispuestos a aceptar cambios importantes antes, cuando el comité finalmente toma una dirección diferente, realmente no veo por qué esta característica es tan diferente.

async/await también se introdujo en TypeScript en una etapa temprana.

Dicho esto, una característica específica de TS no rompería Javascript.

No habrá algo como "característica específica de TS", consulte los objetivos de diseño de TypeScript para obtener más información.

TypeScript llega antes que ES yendo a un camino brillante, podría haber algunos existentes,simplemente se mantienen por compatibilidad.

TypeScript agregó clases, módulos, lambdas, todo mucho antes de que llegaran a ECMAScript.

Y los módulos en particular son una gran debacle que todavía causa confusión. Ese es un ejemplo que el equipo de TypeScript ha usado repetidamente como ejemplo de un _error_ y de adelantarse demasiado pronto. También tenemos campos privados que se avecinan ahora, que nuevamente, he estado agradecido por private durante los últimos 5 años, pero no causará confusión.

Nuevamente, los decoradores han estado disponibles bajo la bandera, pero ahora que los decoradores están llegando a la Etapa 3, será necesario volver a implementar y romper el código para TypeScript. Es por eso que estas cosas _no_ pueden tomarse como probables.

async/await también se introdujo en TypeScript en una etapa temprana.

Una vez que la propuesta de ECMAScript alcanzó la Etapa 3.

Nadie se está portando mal, pero si esta conversación no puede ir a ningún lado más que en círculos (¿y si hubiera una bandera? Sí, somos conscientes de las banderas) o fuera de tema (¿y si TS deja de ser JS? No cinco años después no estamos cambiando nuestra mente en eso), no necesitamos tenerlo. Hemos dicho durante aproximadamente 3 años que implementaremos esto exactamente cuando el comité ES bloquee su semántica.

Una vez más, el repositorio de propuestas es https://github.com/tc39/proposal-opcional-chaining y puede seguir su progreso allí. Trabajaremos fuera de línea para tratar de mejorar las posibilidades de la propuesta en la próxima reunión del TC39 porque realmente queremos que esto también se lleve a cabo.

Actualización: ¡Llegamos a la etapa 2 esta tarde en TC39!

El encadenamiento opcional es la etapa 3

Desbloquear brevemente este solo con fines de celebración

¡Hurra!

No hagas spam...

Puedes enviar un emoji expresando tus sentimientos.

No hagas spam...

Puedes enviar un emoji expresando tus sentimientos.

Además, es divertido ver cómo aumenta el conteo de emojis. ¡Nunca había visto un comentario de problema ganar tanta popularidad tan rápido!

Grabé un pequeño video de las actualizaciones en tiempo real https://youtu.be/JLBrgPjeGhc

¿Alguien puede darme de baja de esta cosa?

@DanielRosenwasser En caso de que no esté bromeando, o para cualquier otra persona que quiera darse de baja y no sepa cómo, está buscando este botón en la barra lateral derecha:

image

Sin mencionar que hay un enlace en el correo electrónico:

image

Era una broma, estoy trabajando en la propuesta.

@RyanCavanaugh después de desbloquear este problema:
martian

¡Realmente emocionado de ver esto finalmente aterrizar! 🎈 🎉

No puedo esperar a la solución rápida de VSCode correspondiente 😆

image

¡Voy a @ @RyanCavanaugh porque probablemente ya no esté suscrito a este hilo y quiero ser grosero! (y @DanielRosenwasser por si acaso)

@kitsonk No seas idiota. Las personas son libres de darse de baja y no ser acosadas.

@kitsonk , ¿Por qué RyanCavanaugh o DanielRosenwasser cancelaron la suscripción a este hilo? Ryan desbloqueó este problema y Daniel respondió tres comentarios sobre ti.

Incluso si se dieron de baja, no hay necesidad de causar más fatiga de notificaciones al enviarles spam.

Aparentemente, GitHub es un lugar terrible para el humor sarcástico, caramba...

¡Muchas gracias a nuestros campeones en TC39 por descubrir los desagradables detalles de esta nueva función de idioma!

thanks

Creo que @kitsonk solo estaba bromeando, al igual que yo. Si bien estamos siendo un poco tontos con la emoción, este es un recordatorio amable para mantener las cosas civilizadas según el CoC.

@DanielRosenwasser
¿Podría tener que probar esto? O @rbuckton tiene otra sucursal existente para eso 🙋🏻‍♂️


Ok, lo tengo https://github.com/microsoft/TypeScript/commits/opcionalChainingStage1 🤦🏻‍♂️

Sí, eso está bastante desactualizado, desafortunadamente.

Me acabo de dar cuenta de que este problema se abrió hace 5 años. :asombrado:

@rbuckton

Sí, eso está bastante desactualizado, desafortunadamente.

¿Podría intentarlo?

Lo siento @jhpratt @MatthiasKunnen Olvidé que no todos compartimos el mismo contexto en GitHub. He estado rebotando por aquí durante mucho tiempo y he pasado tiempo con Ryan y Daniel IRL y participé brevemente en el evento reciente que dio lugar a mi broma interna mal entendida. Disculpas.

Sin embargo, todo este problema muestra una arqueología interesante de los principios de diseño de TypeScript. Ryan lo planteó en un momento en que TypeScript _podría_ haber considerado una sintaxis anterior a una consideración seria en ECMAScript. Sin embargo, fue en ese momento cuando TypeScript internamente estaba aprendiendo algunas lecciones de predicción de la sintaxis de ES2015 que tenían un impacto serio en el lenguaje. El equipo decidió no considerar la inclusión hasta que hubiera una propuesta de TC39 Etapa 3.

Si bien @RyanCavanaugh puede abordarlo, y sé que ha estado cerca de lo que sucedió con la propuesta, sospecho que habría sido poco probable que lo que el equipo hubiera implementado en 2014 hubiera sido 100 % compatible con la propuesta actual de la Etapa 3. . Entonces, si bien ciertamente es una celebración, también agradezca que no tengamos 5 años de código TypeScript con un operador de navegación seguro que no sería del todo coherente con el comportamiento de la propuesta.

🙏

westwing

¿Es este el momento de eliminar la etiqueta Waiting for TC39 ? 🤔

¿Es este el momento de eliminar la etiqueta Waiting for TC39 ? 🤔

Y agrega ship-it

guau guau

Finalmente. Literalmente estaba preguntando sobre el encadenamiento opcional y'day, preguntándome cuándo será la etapa 3, ¡y bam! ¡Gracias a todos los que trabajaron para impulsarlo!

¿Cómo silenciar este hilo? :)

@opnksyn Si no le importa la emoción del spam, hay una función para recibir una notificación cuando se cierre este problema. Esto evitará que todos los comentarios aparezcan en notificaciones como la que acabas de hacer 😄

image

image

¿Alguna solución de emisión ya definida?
Algo como esto podría ser interesante:

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()))));

Prefiero lo que hace Babel, ya que evita crear ámbitos de función innecesarios:

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);

Prefiero lo que hace Babel, ya que evita crear ámbitos de función innecesarios:

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);

Estoy de acuerdo, @ExE-Boss. Siento que evitar crear ámbitos de función innecesarios es ideal (incluso si hace que el código compilado sea un poco feo)

El rendimiento y el cumplimiento de la especificación ES definitivamente deberían ganarle a la legibilidad cuando hablamos de código compilado.

¿Hay alguna razón por la cual el código compilado por Babel se compara con null y void 0 con triples iguales en lugar de un simple == null ?

¿Hay alguna razón por la cual el código compilado por Babel se compara con null y void 0 con triples iguales en lugar de un simple == null ?

@proteria Acabo de echar un vistazo rápido al código de encadenamiento opcional para __Babel__ ; parece que si pasa la opción loose y la establece en un valor real, no produce las comprobaciones estrictas de igualdad

Eso se debe a document.all (o, para ser pedantes, la ranura interna [[IsHTMLDDA]] ), una peculiaridad que recibe un tratamiento especial en el lenguaje por compatibilidad con versiones anteriores.

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

En la propuesta de encadenamiento opcional

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

pero document.all == null ? void 0 : document.all.foo devolvería incorrectamente void 0 . En modo suelto, este detalle de la especificación se descarta por simplicidad/rendimiento/tamaño del código generado, ya que la mayoría de las personas no tienen que lidiar con document.all todos modos.

¿Seguramente el caso document.all podría ser especial? No debería requerir mucho código adicional, solo unas pocas líneas para verificar el objeto y la propiedad.

Excepto que document.all se puede asignar a una variable, y rastrear dónde se usa requiere un sistema de tipos, razón por la cual Babel genera de manera predeterminada:

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

Estoy al tanto. Babel no tiene un sistema de tipos, TypeScript sí. Tal vez no sea tan simple como lo hice sonar, pero me imagino que ya existe un código para ciertas situaciones que es capaz de rastrear el uso.

En realidad, no necesita un sistema de tipos para realizar un seguimiento de las variables document.all , ya que el comportamiento especial está realmente en HTMLAllCollection , no document.all .

Así que deberías poder hacer un cheque instanceof HTMLAllCollection y estarás dorado.

Sí, pero... ¿por qué harías instanceof cuando solo puedes hacer === null || === void 0 ? Seguro que eso es más sencillo.

Por supuesto, solo estaba señalando que no necesita un sistema de tipos para rastrear document.all :)

Personalmente, me siento tentado a decir simplemente romperlo y ver quién se queja, pero está en la especificación, por lo que es más fácil seguir con eso.

@noppa Se puede realizar en tiempo de compilación. Si foo instanceof HTMLAllCollection es true , emite foo === null || foo === void 0 , de lo contrario podemos emitir de forma _segura_ foo == null .

@G-Rath Nos guste o no, obsoleto no significa que no debería funcionar. TypeScript debería seguir siendo compatible con JavaScript.

@jhpratt Pero eso actualmente va en contra de TypeScript Design Non‑Goals .

Además, aún tendría que hacer foo === null || foo === void 0 para cualquier cosa a la que se le pueda asignar HTMLAllCollection , p. any o object , así que no creo que realmente valga la pena.

Supongo que te refieres a no-gol (5)

Agregue o confíe en la información de tipos en tiempo de ejecución en los programas, o emita un código diferente según los resultados del sistema de tipos. En su lugar, fomente patrones de programación que no requieran metadatos en tiempo de ejecución.

Aunque estoy de acuerdo, esto ciertamente emitiría un código diferente según el tipo, es solo para reducir el tamaño del código. Aunque, como ha señalado, no es tan simple como verificar HTMLAllCollection .

Para ser justos, TS _ha_ rechazado un minificador potencial que usa información de tipo, y esto está (más o menos) relacionado: la razón principal para emitir == null es reducir el tamaño del código en función de la información de tipo.

Si esto _no_ está implementado, sería genial si el equipo de lang agregara una opción a tsconfig similar a la opción "suelta" de Babel.

Después de verificar rápidamente, terser convierte automáticamente foo === null || foo === undefined a foo == null , lo cual no es seguro debido a este caso extremo.

Si esto no se implementa, sería genial si el equipo de lang agregara una opción a tsconfig similar a la opción "suelta" de Babel.

De manera relacionada, muchos de nosotros usamos TypeScript para herramientas de compilación y para aplicaciones móviles, ¡ninguna de las cuales tiene que preocuparse por las restricciones del navegador!

De hecho, usamos TS y Babel juntos, por lo que tal vez una de estas opciones debería ser pasar el operador a Babel/tiempo de ejecución subyacente.

@fbartho

De hecho, usamos TS y Babel juntos, por lo que tal vez una de estas opciones debería ser pasar el operador a Babel/tiempo de ejecución subyacente.

no entiendo este comentario No necesita ninguna opción adicional para pasar el operador a Babel; si tiene TypeScript configurado para Babel, ya tiene noEmit: true que ya pasa _todo_ a Babel.

A la implementación de TypeScript de @Zarel Babel le faltan varias características en las que nuestro código base ya se basaba, incluidos los espacios de nombres y las enumeraciones constantes. Estamos usando TSC con emisión habilitada y aplicando Babel como una segunda transformación. (Estamos trabajando para deshacernos de los espacios de nombres, pero no está claro si alguna vez podremos deshacernos de todas las características que no coinciden)

Las personas que vengan a este hilo deben comenzar en el anuncio anterior de la etapa 3 y leer los comentarios que comienzan allí (culpen a GitHub por ocultar toneladas de contenido de usuario sin una forma sencilla de cargar todo)

Gran característica: "encadenamiento opcional" / "navegación segura". Especialmente en modo estricto de TypeScript. Impresionante saber que esto se implementará pronto. ❤️

Esto me trajo aquí y espero que sea apoyado. Solo un caso de uso:

Esperado en TypeScript 3.7.

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

contra

Actualmente en TypeScript 3.5.

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

¿Funcionará esto sin ningún error? ¿O sigue siendo un TypeError: Cannot read property 'setAttribute' of null. ? La ? . se deben cancelar más cadenas después de nulo/indefinido.

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

Espero lo siguiente:
Si el elemento html no existe (nulo). La consola debe registrar One y Two , y no se intenta invocar el método setAttribute . (Sin errores).
¿Entendí eso correctamente?

@domske FYI, esto no es estrictamente una característica de TS; es una característica de JS.

Según la propuesta del TC39 la sintaxis será:

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

La discusión ha comenzado a volverse circular nuevamente, por lo que volvimos al estado bloqueado.

Realmente ruego a cualquiera que tenga la tentación de dejar un comentario en un hilo de GitHub de más de 100 comentarios para que realmente se comprometa a leer todos los comentarios anteriores primero. ¡Probablemente su pregunta, y la respuesta a ella, se encuentren allí!

¿Fue útil esta página
0 / 5 - 0 calificaciones