Typescript: Compatibilidad con las propiedades ES Rest/Spread propuestas

Creado en 21 feb. 2015  ·  96Comentarios  ·  Fuente: microsoft/TypeScript

propuesta es7: https://github.com/sebmarkbage/ecmascript-rest-spread

Propagación de propiedades

Mecanografía

En mi opinión, el objetivo de este método es poder duplicar un objeto y cambiar algunos accesorios, por lo que creo que es particularmente importante en este caso no verificar la declaración de propiedad duplicada:

var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3, y: 4}; // not an error

Tengo un algoritmo de verificación de tipos muy ingenuo para una función similar ( JSXSpreadAttribute ) en mi pequeña bifurcación jsx-typescript : solo copio las propiedades del _objeto extendido_ en la tabla de propiedades cuando encuentro un objeto extendido , y anular esas propiedades si encuentro una declaración con un nombre similar.

emitiendo

jstransform usa Object.assign , babel introduce una cuña:

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

Podríamos forzar o bien la presencia de assign función en ObjectConstructor interfaz, o proporcionar una función similar (con un nombre diferente).

Creo que la solución óptima sería no emitir ningún ayudante en el objetivo es6 (o si se define Object.assign ), y emitir una función auxiliar para es5 , es3 .

var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3};

/// ES6 emit
var obj = {x: 1, y: 2};
var obj1= Object.assign({}, obj, { z: 3 });

//ES3 emit
var __assign = function (target) { 
    for (var i = 1; i < arguments.length; i++) { 
        var source = arguments[i]; 
        for (var key in source) { 
            if (Object.prototype.hasOwnProperty.call(source, key)) { 
                target[key] = source[key];
            } 
        } 
    } 
    return target; 
};

var obj = {x: 1, y: 2};
var obj1= __assign({}, obj, { z: 3 });

Propiedades de descanso

Mecanografía

Para objeto simple, el nuevo tipo es un subtipo de la asignación que no contiene propiedades que se han capturado antes que las demás propiedades:

var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;
obj1// {x: number; y:number};

Si la asignación de desestructuración tiene una declaración de índice, el resultado también tiene una declaración de índice similar:

var obj: { [string: string]: string };
var {[excludedId], ...obj1} = obj;
obj1// { [string: string]: string };

Las declaraciones new/call obviamente no se capturan:

var obj: { (): void; property: string};
var { ...obj1} = obj;
obj1// { property: string };

emitiendo

No es posible emitir _rest properties_ sin una función auxiliar, esta es de babel:

var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;
var __objectWithoutProperties = function(obj, keys) {
    var target = {};
    for (var i in obj) {
        if (keys.indexOf(i) >= 0) continue;
        if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
        target[i] = obj[i];
    }
    return target;
};

var obj = {x:1, y: 1, z: 1};
var z = obj.z;
var obj1 = __objectWithoutProperties(obj, ["z"]);

_Editar: se agregó un pequeño ejemplo de escritura/emisión_

Committed ES Next Fixed Suggestion

Comentario más útil

No estoy seguro de cómo lo enviaremos eventualmente, pero comencé a trabajar en rest/spread en objetos literales y desestructuración . En este momento, el plan es usar nuestro polyfill de __assign para emitir.

Todos 96 comentarios

Probablemente necesitaríamos algunos ejemplos de cómo esto podría emitirse plausiblemente en el nivel inferior (si eso está dentro del alcance).

@RyanCavanaugh , agregué un pequeño ejemplo de emisión y una idea de escritura.
Me gustaría trabajar en eso, ya que de todos modos es más o menos una función jsx y necesitaré agregarlo a mi bifurcación, pero si se agrega al núcleo de TypeScript, es mejor ^^.

Recientemente descubrí esta característica en ES7 a través de este artículo . ¡Tengo que decir que es bastante útil!

¡Me encantaría ver esto en TypeScript!

¿Alguna actualización sobre esto? Me encantaría ver esto en 1.6, ya que sería muy útil cuando se usa con React :smile:

@prabirshrestha para que conste, actualmente tenemos el operador de distribución de propiedades para JSX en master .

@DanielRosenwasser ¿Podría dar más detalles? ¿Eso significa que esto funciona en el maestro con el indicador --jsx? ¿Eso incluye propiedades de descanso o solo propiedades de distribución ?

Lo siento, @mnpenner y @prabirshrestha , sí, quise decir en master . @RyanCavanaugh sabe más sobre esto que yo, pero creo que solo se trata de propiedades de difusión.

JSX admite _atributos_ distribuidos, por ejemplo, <TagName {...spreadedExpr} /> . Nada que ver con el operador de propagación de ES6 más que compartir el mismo token y tener una semántica más o menos equivalente.

Estoy muy interesado tanto en Redux como en React, la manipulación en el estado se hace mucho más fácil con esto. Me encantaría ver esto implementado.

Rest/Spread está aprobado para la Etapa 2 ahora https://github.com/tc39/tc39-notes/blob/master/es7/2015-07/july-30.md

Queremos esperar a que la propuesta llegue a la Etapa 3 antes de abordar esto.

Es muy útil en reaccionar y redux, impleméntelo lo antes posible, por favor :-)

Aquí hay un módulo de ayuda que he estado usando para solucionar esto. No es ideal ya que tienes que especificar todas las teclas dos veces, una en el lado izquierdo y otra como cadenas en el lado derecho, pero no se me ocurre una mejor manera de hacerlo. ¡Cualquier comentario es bienvenido, estoy seguro de que hay un comportamiento de borde que me he perdido!

https://gist.github.com/tomduncalf/fbae862b123445c117cb

¿Es algo por lo que aceptarías un parche? (que se fusionará una vez que alcance la etapa 3). Y si es así, ¿tiene algún consejo sobre dónde comenzar a hacer cambios para respaldarlo?

Esta es la última característica de ES7+ que mi equipo usa mucho y que nos impide cambiar a mecanografiado; Definitivamente me encantaría tener que hacerlo antes, si es posible.

Consideraríamos un PR aquí. Sin embargo, una nota, el cableado del sistema de tipos para esta función no es una tarea sencilla. Si está interesado en examinar esto, lo mantendría incremental y comenzaría analizando, luego emitiendo y luego escribiendo la verificación. @sandersn debería poder ayudarlo si tiene alguna pregunta.

Solo una nota para aquellos interesados ​​en usar esto con Redux. Estoy de acuerdo en que la notación es conveniente. Pero en realidad me las he arreglado bien sin él aprovechando la biblioteca updeep . Creé algunos tipos básicos para él y funciona bastante bien. Incluso tengo una biblioteca simple que implementa una tienda y acciones basadas en updeep . Si alguien está interesado en esto, sólo póngase en contacto conmigo.

En cualquier caso, :+1: para operadores de propagación en TS. :sonrisa:

@xogeny Estaría muy interesado en echar un vistazo a esa biblioteca. Gracias

@cur3n4 Tengo un repositorio donde he estado jugando con estas ideas. Cambia bastante (ya que todo esto sigue evolucionando). Estaba usando módulos especiales que aprovecharon updeep inicialmente y esa funcionalidad todavía está en la biblioteca . Pero dejé de usarlo porque me resultó difícil mantener las restricciones de tipo necesarias. Sospecho que si Typescript tuviera algo similar a las restricciones de tipo genérico super de updeep (y varias otras bibliotecas, como lodash o ramda ) podría ser hecho mucho más tipo seguro.

Pero no me malinterpreten, sigo pensando que un operador de propagación sería realmente útil. De hecho, le permitiría expresar y hacer cosas de forma segura y concisa en un lenguaje que no veo cómo hacerlo con el sistema de tipo actual (_es decir,_ permitiéndole describir las restricciones de tipo correctas en bibliotecas externas). Por el momento, sacrifico la concisión por la seguridad (mientras que el enfoque updeep sacrificó la seguridad por la concisión).

Después de usar Babel durante los últimos meses, convertí mi proyecto a TS y tuve que volver a hacer todo esto. ¡Me encantaría ver esto implementado!

: +1:

Me encantaría ver esto.

+1

debe tener una función, implemente

+1

+1 sintaxis mucho mejor que Object.assign + compatibilidad con JSX

+1 es imprescindible para el desarrollo de reacción para pasar accesorios ( var { checked, ...other } = this.props; . consulte los documentos de reacción

es imprescindible para el desarrollo de reacción

Seamos honestos, es una conveniencia, no una obligación. La propuesta se encuentra en la Etapa 2 con TC39. El equipo de TypeScript ha dejado en claro que no quieren considerar implementar cosas que no se originen en TypeScript hasta que estén en la Etapa 3. El lugar para "+1" es con TC39.

@kitsonk : no puede esperar una buena adopción cuando los marcos como reaccionar / etc. mencionan específicamente este tipo de cosas en sus documentos. La gente comenzará a usar Typescript, verá todos estos problemas y volverá a Babel. Eso fue lo que hice.

Bueno, creo que estamos hablando de un tema más amplio. Ningún proveedor de navegadores implementará nada que no esté en la Etapa 3 (a menos que personalmente les guste). TC39 ha estado tratando de dar cierto nivel de organización a su parte de la web. TypeScript ha sido mordido, enormemente, saltando el arma antes (módulos, y mira el caos que ha causado desde entonces). Respeto que estén tratando de, al menos, poner algo de orientación y estructura en torno a lo que están dispuestos a implementar y cuándo. ¿Hay alguna otra sugerencia de qué estándar debería usarse? No creo que "porque X framework lo menciona en sus documentos" sería bueno.

los marcos como reaccionar / etc. mencionan específicamente este tipo de cosas en sus documentos

Lo cual marcan como solo una propuesta y luego continúan instruyéndote sobre cómo saltar a través de los aros para configurar Babel 6 para usarlo. Preferiría culpar a los marcos por depender de tecnologías sintácticas que solo están en un borrador de especificación. Personalmente, nosotros en Dojo, nos quemamos con Object.observe y nuevamente soy reticente a tratar de depender de las tecnologías antes de que lleguen a la Etapa 3 en Dojo.

La ruta particular es mucho más que solo admitir la transformación de la sintaxis en emisión para TypeScript. Hay todo el tipo de inferencia que necesita seguir con él. He mirado el borrador de especificaciones para entender, pero ¿dónde van los símbolos? ¿A dónde van las propiedades no enumerables? Supongo que en ambos casos simplemente desaparecen en el éter, pero habrá todo tipo de división y fusión de tipos que deberán continuar en segundo plano para esto, por lo que puedo entender totalmente que el equipo de TypeScript diga "bien, esperemos". en esto hasta que TC39 realmente haya pateado los neumáticos en esto".

En general, creo que TS está bastante bien presentado hoy. Hubo un tiempo (alrededor de 1.4 ~ 1.5, creo) en el que me sentí frustrado por la falta de algunas funciones de ES2015, pero a partir de hoy, TS se ha puesto al día bastante bien con el estándar.

Por supuesto, a veces hay envidia de características frente a otros idiomas. Por ejemplo, Babel ofrece prácticamente cualquier propuesta de JS, incluidas las cosas "experimentales". Personalmente, espero con ansias este problema y también el operador de enlace de función.

Pero al mismo tiempo tenemos que reconocer que el equipo de TS se toma la compatibilidad mucho más en serio que Babel. No quieren cambiar la semántica de su código entre versiones, o simplemente eliminar una función, que es lo que hace Babel. Para algunos proyectos (¿empresariales?) Eso es importante.

También puedo entender que MS no quiere invertir una gran cantidad de recursos en funciones que pueden descartarse, me viene a la mente el fiasco de Object.observe . En realidad, dijeron que considerarían un PR en este caso, por ejemplo: guiño:.

En el pasado, algunas funciones se agregaron antes de un cambio de compilador experimental (por ejemplo, asíncrono), lo que significaba que tenía que participar y reconocer que la función podría cambiar o eliminarse en versiones futuras. ¿Tal vez eso podría hacerse nuevamente para las solicitudes más populares?

Sólo una nota rápida aquí. Hemos sido bastante lentos en la adopción de las funciones de ES propuestas en la etapa 0-1, principalmente debido a problemas de compatibilidad con versiones anteriores. Seguimos de cerca las discusiones en las reuniones del TC39 y participamos directamente en las reuniones o en la preparación de propuestas antes de las reuniones; y cuando incorporamos una función, nos gustaría saber qué significa eso para los usuarios que dependen de ella.
Si observa la documentación del proceso TC39, una característica en la etapa 1 puede esperar cambios posteriores a la aceptación "Importantes". Este fue el caso de las clases, módulos, iteradores y casi todas las características no triviales de ES6.
Poniéndome el sombrero de usuario, romper los cambios en la sintaxis puede ser mecánico para responder, mientras que romper los cambios en la semántica puede ser extremadamente difícil de detectar y corregir; esto puede ser un costo enorme para los equipos que usan cualquiera de estas características y/o un obstáculo para adoptar versiones más nuevas de las herramientas. y nos gustaría minimizar eso cuando sea posible. Es por eso que hemos adoptado una política para habilitar funciones de forma predeterminada cuando alcanzan la etapa 3, y bajo una bandera experimental antes de eso (por ejemplo, decoradores).
Habiendo dicho eso, la propiedad de objetos extendida y el descanso están en nuestra hoja de ruta , y planeamos abordarlos en una versión futura.

¿Tal vez valga la pena considerar agregar a la capacidad del compilador para inyectar algunas extensiones de terceros, como se hace en babel?

Otro pensamiento: ¿qué pasa con el soporte para el preprocesamiento de fuentes mecanografiadas (con Babel)?

Si podemos hacer que Babel (o cualquier biblioteca) amplíe los diferenciales de objetos en llamadas Object.assign , ¿eso podría funcionar lo suficientemente bien? Con suerte, también se podría generalizar a otras funciones experimentales implementadas en Babel.

@kitsonk : tema más amplio absolutamente, me gustaría que github tuviera mejores discusiones;). No estoy de acuerdo contigo un poco aunque:

. TypeScript ha sido mordido, enormemente, saltando el arma antes (módulos, y mira el caos que ha causado desde entonces).

Salieron e hicieron su propia implementación reflejando .NET sin usar nada de la comunidad, eso es su culpa.

poner alguna guía y estructura en torno a lo que están dispuestos a implementar y cuándo

implementan decoradores (etapa 1), propiedades de clase (etapa 1), etc.

No creo que "porque X framework lo menciona en sus documentos" sería bueno.

¿Bromeas verdad? react es el marco más popular y tiene una proyección para permanecer así por un tiempo ahora. no apoyar esa voluntad/ha dañado realmente la adopción.

Creo que TypeScript tiene mucho que ver con Babel y tiene muchos desafíos como: realmente difícil comenzar a usarlo con una aplicación existente, migrar de babel a mecanografiado para A2 es una PESADILLA, falta de complementos desacoplados hace que trabajar con Node sea extremadamente difícil.

¿Bromeas verdad? react es el marco más popular y tiene una proyección para permanecer así por un tiempo ahora. no apoyar esa voluntad/ha dañado realmente la adopción.

Tengo mucho respeto por React, pero no estoy bromeando. No es el único juego en la ciudad y ciertamente está en una gran curva de exageración. Hace 6 meses era React + Flux, ahora React + Redux es el sabor del día. Hace un año, Angular y Polymer. Hace dos años fue Backbone and Ember. Hace seis años era Dojo, MooTools, jQuery, gwt, ExtJS, etc...

no empieces guerras de marcos aquí :exclamation:
{..., } sintaxis de

Salieron e hicieron su propia implementación reflejando .NET sin usar nada de la comunidad, eso es su culpa.

@amcdnl ¿A qué te refieres exactamente? Solo puedo suponer que se refiere al módulo como un problema de espacio de nombres, que no tiene nada que ver con .NET y tampoco es uno de los principales puntos débiles con los módulos en TypeScript.

+1 para que esto se incluya en el nodo mecanografiado js lo admite experimentalmente

let mecanografiado = { ... mecanografiado, object_spread }; // Sí, por favor.

Lástima que no haya movimiento en esto, realmente espero que llegue a la etapa 3 pronto. Si es posible, se eliminará el resaltado de errores de sintaxis en VSCode

Me pregunto si Salsa cambia algo en el juego.

Entiendo que TS no quiere implementar funciones inestables antes de tiempo: recursos limitados, protegernos a los usuarios de posibles cambios importantes, etc.

Pero ahora que Salsa es el motor JS predeterminado de VS Code, la presión por una sintaxis JS más nueva (al menos solo la sintaxis, no la escritura completa de TS) va a aumentar. Especialmente dada la popularidad de Babel.

¿Salsa podrá aceptar una sintaxis más amplia más rápido que TS?

+1

¿Esto va a estar en 2.1 en lugar de 2.0? :llorar:
https://github.com/Microsoft/TypeScript/wiki/Roadmap#21

Teniendo en cuenta lo grande que ya está resultando ser 2.0, no estoy demasiado sorprendido.

Tratamos de mantener los lanzamientos con una diferencia de 6 a 8 semanas; así que eso limita lo que puede entrar. Estamos haciendo una refactorización de emisores en este momento, y deberíamos poder agregar esta función una vez que se haya completado.

Entonces, ¿esto está arreglado?

... función de ayuda independiente para atributos de propagación
https://github.com/Microsoft/TypeScript/releases/tag/v1.8.10

Dado que todavía recibo "Se espera un patrón de reestructuración de la propiedad"

No exactamente. Esa solución es específicamente para los atributos de propagación JSX:

const props = { foo: "bar" };
return <SomeComponent {...props} />;

que ya funcionaba, antes de que React v15 eliminara una función interna no documentada de la que dependía el compilador JSX de TypeScript. Ups 😃

La difusión de objetos es una propuesta diferente, con una sintaxis ciertamente similar (incluso puede haber sido propuesta por los ingenieros de Facebook e inspirada en el equivalente de JSX, aunque no estoy seguro).

Entonces, dado que {...props} aún no funciona, ¿cómo podría usar TypeScript para lograr algo similar?

Entonces, dado que {... props} aún no funciona, ¿cómo podría usar TypeScript para lograr algo similar a eso?

Use xtend : https://www.npmjs.com/package/xtend tiene excelentes tipos: https://github.com/typed-typings/npm-xtend (por @blakeembrey :rose :) gracias al tipo de intersección

¡O no use ninguna biblioteca en absoluto!

const newProps = Object.assign({} /*new object*/, props /* add all attributes of props */, {
   // add additional props
  bar: "baz"
});

Es un poco detallado, pero _es_ nativo de ES2015, así que si ya tienes un polyfill para eso, eres sólido.

Este operador es una de las principales razones por las que decidimos usar babel en un proyecto en lugar de mecanografiado.

Cambia completamente el juego para manejar objetos de manera inmutable.

Como alternativa, ¿hay algo planeado que le permita conectar una transformación personalizada a TSC como puede hacer en babel? Tal vez incluso basado en la misma API, eso sería increíble y resolvería problemas como este.

Como alternativa, ¿hay algo planeado que le permita conectar una transformación personalizada a TSC como puede hacer en babel?

@Niondir Sí. Simplemente busque las etiquetas [Transforms] . Estos son necesarios para obtener una transpilación asíncrona/espera/generadora adecuada en el compilador de TypeScript :rose:

Después de una pequeña búsqueda, ¿estás haciendo referencia a esto? https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API

Suena genial. Simplemente no encuentro nada relacionado con Object Spread para esa API. Tal vez sería un buen proyecto para resolver este problema basado en la API del compilador :)

@Niondir lo siento por no brindar una mejor ayuda en mi mensaje original. Estoy hablando de https://github.com/Microsoft/TypeScript/issues/5595 < que permitirá conectar emit para la sintaxis de ESNext. TypeScript es un poco más complicado que Babel porque necesita tener un sistema de tipo _semántico_

Tener un emisor basado en un complemento facilitaría esto. Sí, usaría la API del compilador (o potencialmente fork el compilador al principio hasta que se haga público un sistema de complementos + para la lógica semántica/del escáner)

Esta podría ser una pregunta para otro problema, pero ¿no sería posible agregar una opción "permitir-experimental" y hacer que TypeScript permita cosas futuras? Por ejemplo, permita distribuir el operador y la salida tal cual. Puedo manejar eso con babel después.

Esta es una característica imprescindible para algunos proyectos en los que estoy trabajando y no puedo migrar sin cosas esenciales como esta. Sería genial si pudiera.

No estoy seguro de cómo lo enviaremos eventualmente, pero comencé a trabajar en rest/spread en objetos literales y desestructuración . En este momento, el plan es usar nuestro polyfill de __assign para emitir.

¡Esta es la mejor noticia que escuché en los últimos 3 meses!

Desafortunadamente, resulta que hacer que esto funcione con genéricos es mucho trabajo. Tienes que admitir correctamente un código como este:

function addId<T>(t: T): {...T, id: number} {
    return { ...t, id: 1 };
}

Como puede ver, addId devuelve un nuevo tipo de tipo, un tipo de objeto que incluye un operador de extensión.

Después de un poco de discusión, decidimos retrasar hasta 2.1 y ver esto de nuevo. Tenemos que centrarnos en hacer que las funciones 2.0 estén listas ahora.

Perdone mi ignorancia... pero parece que addId en ese caso podría devolver T & { id: number } ? ¿O hay alguna peculiaridad de los tipos de unión que impide que sea una solución óptima?

Creo que quiere decir que en realidad T debe verificarse, ya que puede pasar cualquier cosa a T

addId<boolean>(true);
addId<number>(5);

Babel ignora silenciosamente tales declaraciones let a = { ...true } pero creo que no deberían ser válidas

No es T & { id: number } porque si T tiene una propiedad id , se anulará.

Por otro lado, la intersección, el tipo resultante de id sería la intersección del tipo T de id y number .

Entonces, lo que dice @sandersn es que necesita un operador de tipo separado.

Si bien definitivamente nos beneficiaríamos de una mejor solución de escritura aquí, Object.assign ya existe y usa ingenuamente las intersecciones como un tipo de salida. ¿Por qué no podemos tener, como una medida provisional, que el operador rest haga exactamente lo que hace Object.assign ?

Aliviaría muchas preocupaciones sobre la función que falta y no generaría un comportamiento no deseado ya que la gente ya usa Object.assign lugar.

En realidad, me sorprendió mucho que esto no fuera compatible.

En cuanto tengamos esto, mejor. ¡Funcionará muy bien con reaccionar!

Sería feliz incluso si no estuviera escrito.

@ 2426021684 Puede sonar razonable, pero considere las implicaciones. Si no se escribe, es decir, se escribe como any , cualquier uso de la sintaxis filtrará any a las hojas de las expresiones.

@aluanhaddad sería lo mismo que Object.assign que es la solución alternativa que todos deben usar porque no tienen otra opción (ver la publicación de JabX arriba)

Considere implementar la sugerencia de @JabX a corto plazo. Esta propuesta es la etapa 3 en todo menos en el nombre, _definitivamente_ va a ser parte del estándar y tiene una semántica muy clara y simple. Poner en marcha el soporte sintáctico para diferenciales de propiedad + escritura ingenua de Object.assign sería un recurso provisional muy útil mientras esperamos la implementación "correcta". No dejes que lo perfecto sea enemigo de lo bueno.

Las propiedades de descanso son extremadamente importantes con React 15.2, como se puede ver aquí https://facebook.github.io/react/warnings/unknown-prop.html

const divProps = Object.assign({}, props)
delete divProps.layout

es muy feo, especialmente si la cantidad de accesorios en un componente es mayor

para aquellos que no pueden esperar más aquí hay una solución alternativa:

function steal(result: any, data: any): any {
    for (var key in data) {
        if (value.hasOwnProperty(key)) {
            result[key] = data[key];
        }
    }
    return result;
}

export class SameAs<a> {
    constructor(public result: a) { }
    public and<b>(value: b): SameAs<a & b> {
        return new SameAs<a & b>(steal(this.result, value));
    }
}
export function sameAs<a>(value: a): SameAs<a> {
    return new SameAs(steal({}, value));
}

// example of use:

const mixture = sameAs(one).and(anotherOne).and(yetAnotherOne).result; // <-- ta-da!

Ignore esta publicación, vea a continuación para una mejor implementación

Esto es lo que se me ocurrió para la operación de desestructuración de un codificador pobre (mecanografiado):

declare interface ObjectConstructor {
  destruct<T extends Object>(data: T, props: any): T;
  destruct<T extends Object>(data: T, ...propNames: string[]): T;
}

function destruct<T extends Object>(data: T, ...removals: string[]) {
  const rest = <T>{};

  const keys = removals.length === 1 && typeof removals[0] === 'object' ?
    Object.getOwnPropertyNames(removals[0]) :
    <string[]>removals;

  Object
    .getOwnPropertyNames(data)
    .filter(x => keys.indexOf(x) < 0)
    .forEach(x => {
      (<any>rest)[x] = (<any>data)[x];
    });

  return rest;
}

Object.destruct = destruct;

// Usage example:

const srcObj = { A: 'a', B: 'b', C: 'c' };
// destruct using an object template
const onlyC = Object.destruct(srcObj, { A: null, B: null });
// destruct using property names
const onlyA = Object.destruct(srcObj, 'B', 'C');

La ventaja obvia de usar un diseño de objeto es que puede escribir el diseño y, como mínimo, tener conflictos de tipo si refactoriza algo.

Actualizar (también ignore esto, vea a continuación para aún mejor)

He reelaborado esto (después de jugar con él en un entorno del mundo real) y se me ocurrieron algunas funciones mucho más fáciles de trabajar.

function deconstruct<TResult, TData>(
  result: TResult,
  data: TData,
  onHit: (result: TResult, data: TData, x: string) => void,
  onMiss: (result: TResult, data: TData, x: string) => void,
  propNames: string[]
  ) {

  Object
    .getOwnPropertyNames(data)
    .forEach(x => {
      if (propNames.indexOf(x) < 0) {
        if (onMiss != null) {
          onMiss(result, data, x);
        }
      }
      else {
        if (onHit != null) {
          onHit(result, data, x);
        }
      }
    });

  return result;
}

// shallow clone data and create a destructuring array of objects
// i.e., const [ myProps, rest] = destruct(obj, 'propA', 'propB');
function destruct<T>(data: T, ...propNames: string[]) {
  return deconstruct(
    [ <T>{}, <T>{} ],
    data,
    (r, d, x) => (<any>r[0])[x] = (<any>d)[x],
    (r, d, x) => (<any>r[1])[x] = (<any>d)[x],
    propNames
  );
}

// shallow clone data and create a destructuring array of properties
// i.e., const [ propA, propB, rest] = destructProps(obj, 'propA', 'propB');
function destructProps(data: any, ...propNames: string[]) {
  return deconstruct(
    [ <any>{} ],
    data,
    (r, d, x) => r.splice(r.length - 1, 0, d[x]),
    (r, d, x) => r[r.length - 1][x] = d[x],
    propNames
  );
}

// shallow clone data and remove provided props
// i.e., const excluded = omit(obj, 'excludeA', 'excludeB');
function omit<T>(data: T, ...propNames: string[]) {
  return deconstruct(
    <T>{},
    data,
    null,
    (r, d, x) => (<any>r)[x] = (<any>d)[x],
    propNames
  );
}

La escritura en estas funciones hace que sea mucho más fácil trabajar con ellas. Estoy trabajando específicamente con estos en React, por lo que tener una escritura fuerte en mis accesorios conocidos es muy útil. Hay una pieza de código adicional que usa estas funciones que es particularmente útil para reaccionar:

const [ props, restProps ] = destruct(omit(this.props, 'key', 'ref'), 'id', 'text');

En este caso, props se escribe como this.props , pero no contiene ninguno de los accesorios destinados a transferirse más adelante (que se encuentran ahora en restProps )

En mi opinión, no se gana mucho al publicar soluciones alternativas no seguras para este problema.

De acuerdo, estoy trabajando en una versión más segura en este momento.

Otra puñalada en esto:

La extensión del objeto principal:

declare interface ObjectConstructor {
    rest<TData, TProps>(data: TData, propsCreator: (x: TData) => TProps, ...omits: string[]): { rest: TData, props: TProps };
}

function rest<TData, TProps>(data: TData, propsCreator: (x: TData) => TProps, ...omits: string[]) {
  const rest = <TData>{};
  const props = <TProps>propsCreator.apply(this, [ data ]);

  Object
    .getOwnPropertyNames(data)
    .filter(x => props.hasOwnProperty(x) === false && omits.indexOf(x) < 0)
    .forEach(x => {
      (<any>rest)[x] = (<any>data)[x];
    });

  return {
    rest,
    props,
  };
}

Object.rest = rest;

Una extensión específica de React:

declare module React {
  interface Component<P, S> {
    restProps<T>(propsCreator: (x: P) => T, ...omits: string[]): { rest: P, props: T };
  }
}

function restProps<P, S, T>(propsCreator: (x: P) => T, ...omits: string[]) {
  return Object.rest((<React.Component<P, S>>this).props, propsCreator, ...omits.concat('key', 'ref'));
}

React.Component.prototype.restProps = restProps;

Algunos ejemplos de uso:

const src = { A: 'a', B: 'b', C: 'c' };
const { rest, props } = Object.rest(src, x => {
    const props = { A, C } = x;
  return { A, C };
});

Y una muestra del uso de la extensión reaccionar:

const { rest, props } = this.restProps(x => {
  const { header, footer } = x;
  return { header, footer };
});

Toda escritura debe ser preservada. la única parte que me molesta es el creador de accesorios porque requiere duplicación. Creo que podría hackearlo y asumir que el objeto desestructurado vive en _a pero eso parece _peligroso_...
_NOTA_ : _a ni siquiera es un hack viable, ya que sería equivalente a x .

Aquí hay un violín para jugar.

Estoy pensando que devolver una matriz podría tener más sentido, ya que puede cambiar el nombre de las salidas según sea necesario.

elimine eso, esto evitaría obtener la escritura adecuada para los accesorios.

Dado que la mayoría de las personas aquí quieren usar esto con reaccionar, ¿por qué no implementar esto solo para archivos * .tsx hasta que llegue a la etapa 3?

Los reductores de Redux también se pueden escribir como archivos *.tsx, soloobjeto las instancias de encasillamiento deben convertirse a obj como Tipo

Esto es muy útil no solo para reaccionar, sino también en reductores de redux, por ejemplo.

Creo que las etiquetas y el hito faltante en esto están un poco desactualizados. Porque esto está confirmado y se está trabajando (# 10727) para TypeScript 2.1. Así que ya no es necesario debatir su valor.

ping @mhegazy

Actualizamos las etiquetas. Debo aclarar que la razón por la que esto no se ha implementado aún no es el valor, sino la complejidad de la implementación del sistema de tipos. la transformación en sí misma es bastante trivial, es decir, {...x} en Object.assign({}, x) . el problema es cómo se prueban estos tipos y cómo se comportan. por ejemplo, para un parámetro de tipo genérico T es {...T} asignable a T , ¿qué pasa con {x: number, ...T} , qué pasa si T tiene métodos, etc.. https://github.com/Microsoft/TypeScript/issues/10727 proporciona una explicación más detallada de los cambios necesarios en el sistema de tipos.

Aprecio plenamente que los aumentos del sistema de tipos necesarios para un tratamiento ideal de esta característica no son triviales, ¡y es genial ver que se están abordando con entusiasmo! Solo reiteraría que,

poner en marcha el soporte sintáctico para la distribución de propiedades + la escritura ingenua de Object.assign sería un recurso provisional muy útil mientras esperamos la implementación "correcta". No dejes que lo perfecto sea enemigo de lo bueno.

Parece que la propuesta ha llegado a la etapa 3: https://github.com/tc39/proposals

@ddaghan tu ejemplo con tsx no funcionará

cualquier plazo para esta característica?

@ SpyMaster356 He estado al acecho por un tiempo y parece que está cerca. @sandersn ha estado pateando traseros en esto durante (al menos) las últimas semanas. 🙌

Puede seguir aquí (https://github.com/Microsoft/TypeScript/pull/12028) y aquí (https://github.com/Microsoft/TypeScript/pull/11150)

Alguien debería actualizar la hoja de ruta

Parece que el uso de esta función permite la asignación de accesorios desconocidos:

interface State {
  a: string;
  b: number;
}

let state: State = { a: "a", b: 0 };

state = {...state, x: "x"}; // No error

Esperaba un error de que x no es una propiedad conocida de State . ¿No es así como funciona la característica?

Por ejemplo, mi solución actual antes de este PR fue esta:

state = update(state, { x: "x" }); // Error: Property 'x' is missing in type 'State'.

function update<S extends C, C>(state: S, changes: C): S {
  return Object.assign({}, state, changes);
}

¿Es posible lograr esto con la extensión/reposo de objetos?

Object Rest and Spread, según la propuesta de ES , se comporta de manera similar a Object.assign . la última mención de la propiedad "gana". no se reportan errores. en el ejemplo que tenía, el tipo de {...state, x:"X"} es { a: string, b:number, x:string } ; y este tipo se puede asignar a State y, por lo tanto, la asignación funciona. esto es lo mismo que decir que let state: State = { a: "a", b:0, x: "X" }; estarían permitidos.

Pero eso es lo que me confunde: let state: State = { a: "a", b:0, x: "X" } da el error Object literal may only specify known properties, and 'x' does not exist in type 'State' que es lo que quiero... ¿por qué es una asignación válida cuando sale de un objeto literal que contiene un pliego?

lo siento... los objetos literales son un caso especial. mi ejemplo estaba mal. aquí hay un mejor ejemplo:

let obj = { a: "a", b:0, x: "X" };
let state: State = obj; // OK

El problema aquí es bastante subjetivo. Cuando el compilador ve let state:State = { a: "a", b:0, x: "X" } , lo más probable es que x sea ​​un error tipográfico, ya sea una propiedad obsoleta que se dejó después de la refactorización o un tipo para una propiedad opcional, es por eso que se informa como un error.

sin embargo, si distribuye un objeto, digamos let myConfig : State= { a: 1, ...myOtherBigConfigBag} , si el myOtherBigConfigBag tiene algunas propiedades que no le interesan, solo necesita el a y el b , un error aquí lo obligaría a mantener estas dos interfaces sincronizadas o enviarlas para que estos errores desaparezcan.

eso dijo deberíamos reconsiderar esta decisión. presentó https://github.com/Microsoft/TypeScript/issues/12717 para rastrear eso.

Veo. Me encanta tu idea en #12717, eso es exactamente lo que estaba pensando. De hecho, me gustaría ese comportamiento incluso para los accesorios de propagación, pero puedo ver su punto de que es muy subjetivo y podría ser molesto para algunos casos de uso comunes de JS.

@aaronbeall Estoy de acuerdo, sería molesto para los casos de uso comunes de JS ... La mayoría de las veces, solo desea asegurarse de que el objeto tenga la forma de la interfaz especificada y no inspeccionar directamente el objeto extendido, la implementación actual está bien IMO

Hola chicos, Felicidades por el gran lanzamiento! Ahora es el momento de un merecido descanso... hablando de eso, tengo un problema con el operador de descanso :)

Al usar React, normalmente tiene un componente como:

export interface MyLinkProps extends React.HTMLAttributes {
    myUrl: string
}

class MyLink{

    render(){
      const {myUrl, ...attrs } = this.props;
     return <a href={calculate(myUrl)} ...attrs>Click here</a>;
   }
}

El problema es que cuando pasa el mouse sobre attrs obtiene la lista de todas las propiedades (cientos) en lugar de React.HtmlAttributes.

Sé que el texto mecanografiado tiene un tipo estructural y todo eso, pero ¿podría ser posible asignar un tipo explícito a la variable de resto?

Algunas alternativas:

    const {myUrl, ...attrs as React.HtmlAttributes } = this.props; //Is not really a casting

    const {myUrl, ...attrs : React.HtmlAttributes } = this.props; //conflicts with ES6?

    const attrs : React.HTMLAttributes; 
    const { myUrl, ...attrs } = this.props;  //re-declare the variable

@bondz Bueno, no es cierto en el 100% de los usos en mi proyecto. :) Pero en otro proyecto puede ser muy molesto. En mi caso, estoy usando Redux y React y estoy haciendo un uso intensivo del estado inmutable, lo que significa que para actualizar o crear objetos de estado debo copiar todos los accesorios en un nuevo objeto literal... en todos los casos quiero 100% de seguridad de tipo que yo Estoy tratando de copiar los datos correctos en la interfaz de estado de destino. Actualmente uso funciones para garantizar esta seguridad, pero preferiría usar la distribución de objetos porque es más limpio (sintaxis estándar legible, expresiva). estructuras, así que veo que es bastante subjetivo. Creo que la sugerencia # 12717 es un buen término medio (y más consistente con la seguridad de tipo literal de objeto existente).

https://github.com/Microsoft/TypeScript/issues/2103#issuecomment-145688774
Queremos esperar a que la propuesta llegue a la Etapa 3 antes de abordar esto.

Parece que ya es el estado 3, ¿algún plan para apoyar esto?

Por cierto, usamos VSCode principalmente para el desarrollo de ES, todavía no de TS :)

@evisong esta característica se envió y ya está disponible en la última versión de vsCode. Actualice su vsCode a la versión 1.8

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