Typescript: Suporta propriedades ES Rest/Spread propostas

Criado em 21 fev. 2015  ·  96Comentários  ·  Fonte: microsoft/TypeScript

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

Propriedades de propagação

Digitando

Na minha opinião, o objetivo deste método é poder duplicar um objeto e alterar algumas props, então acho que é particularmente importante neste caso não verificar a declaração de propriedade duplicada:

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

Eu tenho um algoritmo de verificação de tipo muito ingênuo para um recurso semelhante ( JSXSpreadAttribute ) no meu pequeno fork jsx-typescript : eu apenas copio as propriedades do _spread object_ na tabela de propriedades quando encontro um objeto de propagação e substituir essas propriedades se eu encontrar uma declaração com um nome semelhante.

Emitindo

jstransform use Object.assign , babel introduza um shim:

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

Poderíamos quer forçar a presença de assign função em ObjectConstructor interface, ou proporcionar uma função semelhante (com um nome diferente).

Eu acho que a solução ideal seria não emitir nenhum auxiliar no destino es6 (ou se Object.assign estiver definido) e emitir uma função 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 });

Propriedades de descanso

Digitando

Para objeto simples, o novo tipo é um subtipo da atribuição que não contém propriedades que foram capturadas antes das propriedades restantes:

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

Se a atribuição de desestruturação tiver uma declaração de índice, o resultado também terá uma declaração de índice semelhante:

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

declarações new/call obviamente não são capturadas:

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

Emitindo

Não é possível emitir _rest properties_ sem uma função auxiliar, esta é do 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"]);

_Edit: adicionado um pequeno exemplo de digitação/emissão_

Committed ES Next Fixed Suggestion

Comentários muito úteis

Não tenho certeza de como vamos enviá-lo, mas comecei a trabalhar em rest/spread em literais de objeto e desestruturando arquivos . No momento, o plano é usar nosso polyfill __assign para emissão.

Todos 96 comentários

Provavelmente precisaríamos de alguns exemplos de como isso poderia ser emitido de forma plausível em nível inferior (se estiver no escopo).

@RyanCavanaugh , adicionei um pequeno exemplo de emissão e ideia de digitação.
Eu gostaria de trabalhar nisso, pois de qualquer maneira é mais ou menos um recurso jsx e precisarei adicioná-lo ao meu fork, mas se for adicionado ao núcleo do typescript é melhor ^^.

Recentemente, descobri esse recurso no ES7 por meio deste artigo . Devo dizer que é bastante útil!

Adoraria ver isso no TypeScript!

Alguma atualização sobre isso? Adoraria ver isso em 1.6, pois seria super útil ao usar com React :smile:

@prabirshrestha para registro, atualmente temos o operador de propagação de propriedade para JSX em master .

@DanielRosenwasser Você poderia elaborar? Isso significa que isso funciona no master com o sinalizador --jsx? Isso inclui propriedades de descanso ou apenas propriedades de propagação ?

Desculpe @mnpenner e @prabirshrestha , sim, eu quis dizer em master . @RyanCavanaugh sabe mais sobre isso do que eu, mas acredito que sejam apenas propriedades espalhadas.

JSX suporta spread _attributes_, por exemplo <TagName {...spreadedExpr} /> . Nada a ver com o operador de propagação ES6 além de compartilhar o mesmo token e ter uma semântica aproximadamente equivalente.

Estou muito interessado em Redux e React, a manipulação no estado fica muito mais fácil com isso. Adoraria ver isso implementado.

Rest/Spread está aprovado para o Estágio 2 agora https://github.com/tc39/tc39-notes/blob/master/es7/2015-07/july-30.md

Queremos esperar que a proposta chegue ao Estágio 3 antes de abordar isso.

É muito útil em react e redux, por favor implemente-o o mais rápido possível, por favor :-)

Aqui está um módulo auxiliar que tenho usado para contornar isso. Não é o ideal, pois você precisa especificar todas as teclas duas vezes, uma no lado esquerdo e outra como strings no lado direito, mas não consigo pensar em uma maneira melhor de fazer isso. Qualquer feedback é bem-vindo, tenho certeza de que perdi um comportamento de borda!

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

Isso é algo para o qual você aceitaria um patch? (a ser mesclado quando atingir o estágio 3). E em caso afirmativo, tem alguma indicação de onde começar a fazer alterações para apoiá-lo?

Este é o último recurso do ES7+ que minha equipe usa fortemente e que nos impede de mudar para o texto datilografado; definitivamente adoraria ter que mais cedo, se possível.

Nós consideraríamos um PR aqui. Uma observação, porém, conectar o sistema de tipos para esse recurso não é uma tarefa simples. Se você estiver interessado em ler isso, eu o manteria incremental e começaria analisando, em seguida, emitindo e verificando o tipo. @sandersn deve poder ajudá-lo se você tiver alguma dúvida.

Apenas uma nota para aqueles interessados ​​em usar isso com o Redux. Concordo que a notação é conveniente. Mas, na verdade, estou passando muito bem sem isso, aproveitando a biblioteca updeep . Eu criei algumas tipagens básicas para ele e funciona muito bem. Eu até tenho uma biblioteca simples que implementa uma loja e ações baseadas em updeep . Se alguém estiver interessado nisso, é só entrar em contato comigo.

Em qualquer caso, :+1: para operadores de spread em TS. :sorriso:

@xogeny Eu estaria muito interessado em dar uma olhada nessa biblioteca. Obrigado

@cur3n4 Eu tenho um repositório onde tenho brincado com essas ideias. Muda bastante (já que tudo isso ainda está evoluindo). Eu estava usando um módulo especial que alavancou updeep inicialmente e essa funcionalidade ainda está na biblioteca . Mas parei de usá-lo porque achei difícil manter as restrições de tipo necessárias. Eu suspeito que se o Typescript tivesse algo parecido com as restrições de tipo genérico super do updeep (e várias outras bibliotecas, como lodash ou ramda ) poderia ser fez muito mais tipo seguro.

Mas não me entenda mal, ainda acho que um operador de spread seria muito útil. Na verdade, isso permitiria que você expressasse e fizesse coisas de forma segura e concisa na linguagem que não vejo como fazer com o sistema de tipos atual (_ou seja,_ permitindo que você descreva as restrições de tipo corretas em bibliotecas externas). No momento, sacrifiquei a concisão pela segurança (enquanto a abordagem updeep sacrificou a segurança pela concisão).

Depois de usar o Babel nos últimos meses, converti meu projeto para TS e tive que refazer tudo isso. Adoraria ver isso implementado!

: +1:

Adoraria ver isso.

+1

deve ter recurso, por favor implemente

+1

+1 sintaxe muito melhor que Object.assign + suporte a JSX

+1 é obrigatório para o desenvolvimento do react para passar adereços ( var { checked, ...other } = this.props; . veja os documentos do react

é um deve ter para reagir o desenvolvimento

Sejamos honestos, é uma conveniência, não um must have. A proposta está na Fase 2 com TC39. A equipe do TypeScript deixou claro, eles não querem considerar a implementação de coisas que não são originadas no TypeScript até que estejam no Estágio 3. O lugar para "+1" é com TC39.

@kitsonk - você não pode esperar uma boa adoção quando estruturas como react / etc chamam especificamente esses tipos de coisas em seus documentos. As pessoas vão começar a usar o Typescript, ver todos esses problemas e voltar para o Babel. Isso é o que eu fiz.

Bom, acho que estamos falando de um assunto mais amplo. Nenhum fornecedor de navegador implementará nada que não esteja no Estágio 3 (a menos que pessoalmente goste). O TC39 vem tentando dar algum nível de organização para sua parte da web. TypeScript foi mordido, enormemente, saltando a arma antes (módulos, e veja o caos que isso causou desde então). Eu respeito que eles estão tentando pelo menos colocar alguma orientação e estrutura em torno do que eles estão dispostos a implementar e quando. Existe alguma outra sugestão de qual padrão deve ser usado? Eu não acho que "porque o framework X menciona isso em seus documentos" seria uma boa.

frameworks como react / etc chamam especificamente esses tipos de coisas em seus documentos

O que eles marcam como apenas uma proposta e, em seguida, passam a instruí-lo sobre como pular etapas para configurar o Babel 6 para usá-lo. Prefiro culpar os frameworks por dependerem de tecnologias sintáticas que estão apenas em uma especificação preliminar. Pessoalmente, nós no Dojo, fomos queimados por Object.observe e estou reticente novamente em tentar depender de tecnologias antes que elas cheguem ao Estágio 3 no Dojo.

O caminho específico é muito mais do que apenas dar suporte à transformação da sintaxe ao emitir para o TypeScript. Há toda a inferência de tipo que precisa seguir com ela. Eu olhei para a especificação do rascunho para entender, mas para onde vão os símbolos? Para onde vão as propriedades não enumeráveis? Suponho que em ambos os casos eles simplesmente desaparecem no éter, mas haverá todos os tipos de divisão e mesclagem de tipos que precisarão continuar em segundo plano para isso, para que eu possa entender totalmente a equipe do TypeScript dizendo "ok, vamos adiar sobre isso até que o TC39 realmente chutou os pneus nisso".

No geral, acho que o TS está muito bem apresentado hoje. Houve um tempo (em torno de 1,4 ~ 1,5, eu acho) em que fiquei frustrado com a falta de alguns recursos do ES2015, mas a partir de hoje o TS alcançou muito bem o padrão.

Claro, às vezes há inveja de recursos em outros idiomas. Por exemplo, Babel oferece praticamente qualquer proposta JS, incluindo coisas "experimentais". Pessoalmente, estou ansioso por esse problema e também pelo operador de ligação de função.

Mas, ao mesmo tempo, temos que reconhecer que a equipe TS leva a compatibilidade muito mais a sério do que a Babel. Eles não querem alterar a semântica do seu código entre os lançamentos, ou simplesmente remover um recurso, que é o que o Babel faz. Para alguns projetos (empresariais?) isso é importante.

Eu também posso entender que a MS não quer investir muitos recursos em recursos que podem ser descartados, o fiasco Object.observe vem à mente. Na verdade, eles disseram que considerariam um PR neste por exemplo: wink:.

No passado, alguns recursos foram adicionados antecipadamente por trás de uma troca de compilador experimental (por exemplo, async), o que significava que você tinha que aceitar e reconhecer que o recurso poderia mudar ou ser removido em versões futuras. Talvez isso possa ser feito novamente para os pedidos mais populares?

Apenas uma nota rápida aqui. Temos sido bastante lentos na adoção dos recursos de ES propostos no estágio 0-1, principalmente devido a preocupações de compatibilidade com versões anteriores. Acompanhamos de perto as discussões nas reuniões do TC39 e participamos diretamente das reuniões ou da preparação de propostas antes das reuniões; e quando colocamos um recurso, gostaríamos de saber o que isso significa para os usuários que dependem deles.
Se você observar a documentação do processo TC39, um recurso no estágio 1 pode esperar mudanças "Grandes" após a aceitação. Este foi o caso de classes, módulos, iteradores e quase todos os recursos não triviais do ES6.
Colocando meu chapéu de usuário, alterações de sintaxe podem ser mecânicas para responder, enquanto alterações de semântica podem ser extremamente difíceis de detectar e corrigir; isso pode ser um custo enorme para as equipes que usam qualquer um desses recursos e/ou um bloqueador para a adoção de versões mais recentes das ferramentas. e gostaríamos de minimizar isso quando possível. É por isso que adotamos uma política para habilitar recursos por padrão quando atingem o estágio 3, e sob um sinalizador experimental antes disso (por exemplo, decoradores).
Dito isso, a propriedade do objeto spread e rest estão em nosso roteiro e planejamos abordá-los em uma versão futura.

Talvez valha a pena considerar adicionar ao compilador a capacidade de injetar algumas extensões de terceiros, como feito no babel ?

Outro pensamento: e quanto ao suporte para pré-processamento de fontes typescript (com Babel)?

Se pudermos fazer com que o Babel (ou qualquer biblioteca) expanda os spreads de objetos em chamadas Object.assign , isso pode funcionar bem o suficiente? Espero que seja generalizável para outros recursos experimentais implementados no Babel também.

@kitsonk - tópico mais amplo absolutamente, gostaria que o github tivesse discussões melhores;). Eu discordo um pouco de você:

. TypeScript foi mordido, enormemente, saltando a arma antes (módulos, e veja o caos que isso causou desde então).

Eles foram e fizeram sua própria implementação espelhando .NET não usando nada da comunidade, isso é culpa deles.

colocar alguma orientação e estrutura em torno do que eles estão dispostos a implementar e quando

eles implementam decoradores (estágio 1), propriedades de classe (estágio 1), etc.

Eu não acho que "porque o framework X menciona isso em seus documentos" seria uma boa.

está brincando certo? react é o framework mais popular e tem uma projeção para ficar assim por algum tempo agora. não apoiar isso realmente prejudicou a adoção.

Acho que o TypeScript tem muito a ver com o Babel e tem muitos desafios, como: muito difícil começar a usá-lo com um aplicativo existente, migrar do babel para o typescript para A2 é um PESADELO!, falta de plugins desacoplados torna o trabalho com Node extremamente difícil.

está brincando certo? react é o framework mais popular e tem uma projeção para ficar assim por algum tempo agora. não apoiar isso realmente prejudicou a adoção.

Eu tenho muito respeito pelo React, mas não estou brincando. Não é o único jogo na cidade e certamente está em uma grande curva de hype. 6 meses atrás era React + Flux, agora React + Redux é o sabor do dia. Há um ano, Angular e Polímero. Dois anos atrás era Backbone e Ember. Há seis anos atrás era Dojo, MooTools, jQuery, gwt, ExtJS, etc...

não comece guerras de framework aqui :exclamation:
{..., } sintaxe

Eles foram e fizeram sua própria implementação espelhando .NET não usando nada da comunidade, isso é culpa deles.

@amcdnl A que exatamente você está se referindo? Só posso supor que você esteja se referindo ao módulo como problema de namespace, que não tem nada a ver com .NET e também não é um dos principais pontos problemáticos dos módulos no TypeScript.

+1 para que isso seja incluído no nó datilografado js está suportando-o experimentalmente

let typescript = { ...typescript, object_spread }; // Sim por favor.

Que pena, não há movimento nisso, eu realmente espero que isso chegue ao estágio 3 em breve. Vai matar o destaque do erro de sintaxe no VSCode, se possível

Gostaria de saber se a Salsa muda alguma coisa no jogo?

Entendo que o TS não queira implementar recursos instáveis ​​antecipadamente: recursos limitados, protegendo os usuários de possíveis alterações de quebra, etc.

Mas agora que o Salsa é o mecanismo JS padrão do VS Code, a pressão por uma sintaxe JS mais nova (pelo menos apenas a sintaxe, não a digitação TS completa) vai aumentar. Especialmente dada a popularidade de Babel.

O Salsa será capaz de aceitar uma sintaxe mais ampla mais rapidamente que o TS?

+1

Isso vai ser em 2.1 em vez de 2.0? :choro:
https://github.com/Microsoft/TypeScript/wiki/Roadmap#21

Considerando o quão grande o 2.0 já está se tornando, não estou muito surpreso.

Tentamos manter os lançamentos com 6-8 semanas de intervalo; então isso limita o que pode entrar. estamos fazendo alguma refatoração de emissor no momento, e devemos ser capazes de adicionar esse recurso assim que estiver concluído.

Então isso é fixo?

...função auxiliar autônoma para atributos de propagação
https://github.com/Microsoft/TypeScript/releases/tag/v1.8.10

Como ainda estou recebendo "Padrão de reestruturação de propriedade esperado"

Não exatamente. Essa correção é especificamente para atributos de propagação JSX:

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

que já funcionava, antes do React v15 remover uma função interna não documentada da qual o compilador JSX do TypeScript dependia. Opa 😃

Object spread é uma proposta diferente, com uma sintaxe reconhecidamente similar (pode até ter sido proposta por engenheiros do Facebook e inspirada no equivalente JSX, embora eu não tenha certeza).

Então, como {...props} ainda não funciona, como você usaria o TypeScript para obter algo semelhante a isso?

Então, como {...props} ainda não funciona, como você usaria o TypeScript para obter algo semelhante a isso

Use xtend : https://www.npmjs.com/package/xtend tem ótimas digitações : https://github.com/typed-typings/npm-xtend (por @blakeembrey :rose:) obrigado para o tipo de interseção

Ou não use nenhuma biblioteca!

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

É um pouco verboso, mas _é_ nativo do ES2015, então se você já tem um polyfill para isso, você é sólido.

Este operador é uma das principais razões pelas quais decidimos usar o babel em um projeto ao invés do typescript.

Muda completamente o jogo para manipular objetos de forma imutável.

Como alternativa, há algo planejado que permita conectar a transformação personalizada ao TSC, como você pode fazer no babel? Talvez até baseado na mesma API, isso seria incrível e resolveria problemas como esse.

Como alternativa, há algo planejado que permita conectar a transformação personalizada ao TSC, como você pode fazer no babel

@Niondir Sim. Basta procurar as tags [Transforms] . Eles são necessários para obter a transpilação async/await/generator adequada para o compilador TypeScript: rose:

Depois de uma pequena pesquisa, você está referenciando isso? https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API

Parece ótimo. Só não encontrei nada relacionado ao Object Spread para essa API. Talvez seja um bom projeto para resolver esse problema com base na API do compilador :)

@Niondir desculpe por não fornecer uma ajuda melhor na minha mensagem original. Estou falando de https://github.com/Microsoft/TypeScript/issues/5595 < que permitirá emit conectável para a sintaxe ESNext. O TypeScript é um pouco mais complicado do que o Babel porque ele precisa ter uma compreensão do sistema do tipo _semantic_ do seu código, de modo que o trabalho de propagação de objetos provavelmente precisará vir da equipe do TypeScript.

Ter um emissor baseado em plugin tornaria isso mais fácil. Sim, você usaria a API do compilador (ou potencialmente fork o compilador no início até que um sistema de plugin seja tornado público + para a lógica semântica/scanner)

Isso pode ser uma pergunta para outro problema, mas não seria possível adicionar uma opção "permitir experimental" e fazer com que o TypeScript permitisse coisas futuras? Por exemplo, permita o operador de propagação e a saída como está. Eu posso lidar com isso com babel depois.

Este é um recurso obrigatório para alguns projetos em que estou trabalhando e não posso migrar sem coisas essenciais como esta. Seria incrível se eu pudesse.

Não tenho certeza de como vamos enviá-lo, mas comecei a trabalhar em rest/spread em literais de objeto e desestruturando arquivos . No momento, o plano é usar nosso polyfill __assign para emissão.

Esta é a melhor notícia que ouvi nos últimos 3 meses!

Infelizmente, acontece que fazer isso funcionar com genéricos é muito trabalho. Você tem que suportar adequadamente o código como este:

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

Como você pode ver, addId retorna um novo tipo de tipo, um tipo de objeto que inclui um operador de spread.

Depois de alguma discussão, decidimos adiar até 2.1 e olhar para isso novamente. Precisamos nos concentrar em fazer os recursos 2.0 agora.

Perdoe minha ignorância... mas parece que addId nesse caso pode retornar T & { id: number } ? Ou existe alguma peculiaridade dos tipos de união que impede que isso seja uma solução ideal?

Eu acho que ele quis dizer que na verdade T deve ser verificado, já que você pode passar qualquer coisa para T

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

Babel silenciosamente ignora tais declarações let a = { ...true } mas eu acho que elas não deveriam ser válidas

Não é T & { id: number } porque se T tiver uma propriedade id , ela será substituída.

Por outro lado, a interseção, o tipo resultante de id seria a interseção do tipo T de id e number .

Então, o que @sandersn está dizendo é que você precisa de um operador de tipo separado.

Embora definitivamente tenhamos nos beneficiado de uma solução de digitação melhor aqui, Object.assign já existe e ingenuamente usa interseções como um tipo de saída. Por que não poderíamos ter, como medida de intervalo, o operador rest fazendo exatamente o que Object.assign faz?

Isso aliviaria muitas preocupações sobre o recurso ausente e não traria comportamento indesejado, já que as pessoas já usam Object.assign .

Na verdade, fiquei extremamente surpreso que isso não fosse suportado.

Assim que conseguirmos isso, melhor. Vai funcionar muito bem com reagir!

Eu ficaria feliz mesmo que não fosse digitado

@ 2426021684 Pode parecer razoável, mas considere as implicações. Se não for digitado, ou seja, digitado como any , quaisquer usos da sintaxe passarão any para as folhas das expressões.

@aluanhaddad seria o mesmo que Object.assign que é a solução alternativa que todos precisam usar porque não têm escolha (veja a postagem do JabX acima)

Por favor, considere implementar a sugestão do @JabX no curto prazo. Esta proposta é o estágio 3 em tudo, menos no nome, ela _definitivamente_ fará parte do padrão e tem uma semântica muito clara e simples. Colocar em prática o suporte sintático para spreads de propriedade + digitação ingênua de Object.assign seria um paliativo muito útil enquanto esperamos pela implementação "certa". Não deixe que o perfeito seja inimigo do bom.

As propriedades de descanso são extremamente importantes com o React 15.2, como pode ser visto aqui https://facebook.github.io/react/warnings/unknown-prop.html

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

é muito feio, especialmente se a quantidade de adereços em um componente for maior

para aqueles que não podem esperar mais, aqui está uma solução 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 este post, veja abaixo para uma melhor implementação

Aqui está o que eu criei para a operação de desestruturação de um codificador pobre (datilografado):

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

A vantagem óbvia de usar um layout de objeto é que você pode digitar o layout e, no mínimo, obter conflitos de tipo se você refatorar alguma coisa.

Atualizar (também ignore isso, veja abaixo ainda melhor)

Eu reformulei isso (depois de brincar com isso em um ambiente do mundo real) e criei algumas funções muito mais fáceis de trabalhar.

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

A digitação dessas funções torna as coisas muito mais fáceis de trabalhar. Estou trabalhando especificamente com isso no React, então ter uma digitação forte em meus adereços conhecidos é muito útil. Há um pedaço de código adicional usando essas funções que é particularmente útil para reagir:

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

Nesse caso, props é digitado exatamente como this.props , mas não contém nenhum dos adereços destinados à transferência (que agora estão em restProps )

IMO, não se ganha muito postando soluções alternativas não tipificadas para esse problema.

concordo, estou trabalhando em uma versão mais segura para digitação agora.

Outra facada nisso:

A extensão do 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;

Uma extensão específica do 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;

Alguns exemplos 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 };
});

E uma amostra do uso da extensão react:

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

Toda a digitação deve ser preservada. a única parte que me incomoda é o criador dos adereços porque requer duplicação. Eu acho que poderia hackear e assumir que o objeto desestruturado vive em _a mas isso parece _perigoso_...
_NOTA_ : _a nem é um hack viável, pois seria equivalente a x .

Aqui está um violino para brincar.

Estou pensando que retornar uma matriz pode fazer mais sentido, pois você pode renomear as saídas conforme necessário.

riscar isso, isso impediria a digitação adequada para os adereços.

Já que a maioria das pessoas aqui quer usar isso com react, por que não implementar isso apenas para arquivos *.tsx até chegar ao estágio 3?

Os redutores de redux também podem ser escritos como arquivos *.tsx, apenasobj instâncias de conversão de tipos devem ser convertidas em obj como tipo

Isso é muito útil não apenas para reagir, mas em redutores redux, por exemplo.

Eu acho que as tags e marcos ausentes sobre isso estão um pouco desatualizados. Porque isso está confirmado e sendo trabalhado (#10727) para o TypeScript 2.1. Portanto, não há mais necessidade de debater o valor disso.

ping @mhegazy

Atualizado as etiquetas. Devo esclarecer que a razão pela qual isso ainda não foi implementado não é o valor, mas sim a complexidade da implementação do sistema de tipos. a transformação em si é bastante trivial, ou seja, {...x} em Object.assign({}, x) . a questão é como esses tipos são pré-testados e como eles se comportam. por exemplo, para um parâmetro de tipo genérico T é {...T} atribuível a T , e quanto a {x: number, ...T} , e se T tiver métodos, etc. https://github.com/Microsoft/TypeScript/issues/10727 fornece uma explicação mais detalhada das alterações de sistema de tipo necessárias.

Eu aprecio totalmente que os aumentos de sistema de tipos necessários para um tratamento ideal desse recurso não são triviais, e é ótimo ver que eles estão sendo abordados com entusiasmo! Gostaria apenas de reiterar que,

colocar em prática o suporte sintático para spreads de propriedade + tipagem ingênua de Object.assign seria um paliativo muito útil enquanto esperamos pela implementação "certa". Não deixe que o perfeito seja inimigo do bom.

Parece que a proposta chegou ao estágio 3: https://github.com/tc39/proposals

@ddaghan seu exemplo com tsx não funcionará

algum prazo para este recurso?

@SpyMaster356 Eu estive espreitando isso por um tempo e parece que está perto. @sandersn está

Você pode acompanhar aqui (https://github.com/Microsoft/TypeScript/pull/12028) e aqui (https://github.com/Microsoft/TypeScript/pull/11150)

Alguém deveria atualizar o roteiro

Parece que o uso desse recurso permite a atribuição de adereços desconhecidos:

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

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

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

Eu estava esperando um erro de que x não é uma propriedade conhecida de State . Não é assim que o recurso funciona?

Por exemplo, minha solução atual antes deste PR era 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);
}

É possível conseguir isso com a propagação/descanso do objeto?

O Object Rest and Spread, conforme a proposta do Object.assign . a última menção da propriedade "ganha". nenhum erro é relatado. no exemplo que você teve, o tipo de {...state, x:"X"} é { a: string, b:number, x:string } ; e este tipo é atribuível a State e assim a atribuição funciona. isso é o mesmo que dizer que let state: State = { a: "a", b:0, x: "X" }; também seria permitido.

Mas é por isso que estou confuso: let state: State = { a: "a", b:0, x: "X" } dá o erro Object literal may only specify known properties, and 'x' does not exist in type 'State' que é o que eu quero... por que é uma atribuição válida ao sair de um literal de objeto contendo um spread?

desculpe... literais de objeto são um caso especial. meu exemplo estava errado. aqui está um exemplo melhor:

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

A questão aqui é bastante subjetiva. Quando o compilador vê let state:State = { a: "a", b:0, x: "X" } , é provável que x seja um erro de digitação, seja uma propriedade obsoleta que foi deixada de lado após a refatoração ou um tipo para uma propriedade opcional, por isso é relatado como um erro.

no entanto, se você espalhar um objeto, digamos let myConfig : State= { a: 1, ...myOtherBigConfigBag} , se o myOtherBigConfigBag tiver algumas propriedades que você não se importa, você só precisa do a e do b , um erro aqui forçaria você a manter essas duas interfaces em sincronia ou fazer o cast para fazer com que esses erros desapareçam.

dito isto. devemos reconsiderar esta decisão. arquivado https://github.com/Microsoft/TypeScript/issues/12717 para rastrear isso.

Eu vejo. Adorei sua ideia em #12717, era exatamente o que eu estava pensando. Eu realmente gostaria de tal comportamento, mesmo para os props de propagação, mas posso ver seu ponto de vista de que é muito subjetivo e pode ser irritante para alguns casos de uso comuns de JS.

@aaronbeall Concordo, seria irritante para casos de uso comuns de JS ... Na maioria das vezes, você só quer garantir que o objeto tenha a forma da interface especificada e não inspecionar diretamente o objeto de propagação, a implementação atual está bem IMO

Olá pessoal, parabéns pelo ótimo lançamento! Agora é hora de um descanso merecido... falando nisso estou com um problema com o operador de descanso :)

Usando React você normalmente tem um 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>;
   }
}

O problema é que quando você passa o mouse sobre attrs você obtém a lista de todas as propriedades (centenas) ao invés de React.HtmlAttributes.

Eu sei que o typescript é estruturalmente tipado e tudo mais, mas seria possível atribuir um tipo explícito à variável restante?

Algumas 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 Bem, não é verdade em 100% dos usos no meu projeto. :) Mas em outro projeto pode ser muito chato. No meu caso estou usando Redux e React e fazendo uso pesado do estado imutável, o que significa atualizar ou criar objetos de estado devo copiar todas as props para um novo literal de objeto... em todos os casos eu quero 100% de segurança de tipo que eu estou tentando copiar os dados corretos na interface de estado de destino. Atualmente eu uso funções para garantir essa segurança, mas eu preferiria usar object spread porque é mais limpo (legível, expressivo, sintaxe padrão). estruturas, então eu vejo como é bastante subjetivo. Acho que a sugestão #12717 é um bom meio termo (e mais consistente com a segurança do tipo literal de objeto existente).

https://github.com/Microsoft/TypeScript/issues/2103#issuecomment -145688774
Queremos esperar que a proposta chegue ao Estágio 3 antes de abordar isso.

Parece que já é o estado 3, algum plano de apoio a isso?

Aliás, usamos VSCode principalmente para desenvolvimento ES, não TS ainda :)

@evisong este recurso foi lançado e já está disponível na versão mais recente do vsCode. Atualize seu vsCode para a versão 1.8

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