Typescript: Qualquer roteiro para apoiar Object.assign?

Criado em 9 jun. 2015  ·  47Comentários  ·  Fonte: microsoft/TypeScript

em 1.6 ou 2.0?

Question

Comentários muito úteis

Desculpe, não entendo. ES6 suporta Object.assign então Babel o transpila para um polyfill para destinos ES5. Estou confuso por que o TypeScript não faz o mesmo, assim como faz com outros recursos do ES6.

Todos 47 comentários

não tenho certeza do que você está procurando. se você quer dizer as tipificações, já está definido em lib.es6.d.ts como

    /**
      * Copy the values of all of the enumerable own properties from one or more source objects to a 
      * target object. Returns the target object.
      * <strong i="6">@param</strong> target The target object to copy to.
      * <strong i="7">@param</strong> sources One or more source objects to copy properties from.
      */
    assign(target: any, ...sources: any[]): any;

quer dizer a implementação, o TypeScript não inclui pollyfills, você sempre pode incluir o seu, por exemplo, o Mozilla tem um pollyfill para ele.

Se você está perguntando sobre a digitação do padrão mixin, é algo que sempre tivemos no roadmap para 2.0.

Obrigado. Quero dizer, escrever Object.assign(...) em TypeScript que transpile para es3 / es5 / es6. Portanto, está no roteiro para 2.0.

: +1:

@unional posso ter entendido mal alguma coisa. Mas não acho que sua conclusão esteja correta.

ES6 de qualquer maneira suporta Object.assign , então se seu destino for ES6, você deve ser capaz de escrevê-lo em TypeScript de qualquer maneira.

Mas se você transpilar para es5 e abaixo, ainda precisará de um polyfill, porque es5 não tem Object.assign .

Como uma solução alternativa rápida, considere este polyfill e digitação:

interface ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}

if (typeof Object.assign != 'function') {
  (function () {
    Object.assign = function (target) {
      'use strict';
      if (target === undefined || target === null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }

      var output = Object(target);
      for (var index = 1; index < arguments.length; index++) {
        var source = arguments[index];
        if (source !== undefined && source !== null) {
          for (var nextKey in source) {
            if (source.hasOwnProperty(nextKey)) {
              output[nextKey] = source[nextKey];
            }
          }
        }
      }
      return output;
    };
  })();
}

Obrigado por desenterrar este problema. Já faz algum tempo. :) Sim, agora entendo que ele simplesmente requer um polyfill para es5.

Obrigado novamente!

Vamos compilar este polyfill quando o destino for definido como es5? Quero dizer, como o destino de compilação é definido como es5, isso deve funcionar no ambiente es5.

Compilaremos este polyfill quando o destino for definido como es5

Sim, como um ponyfill: rose:: horse:

Desculpe, não entendo. ES6 suporta Object.assign então Babel o transpila para um polyfill para destinos ES5. Estou confuso por que o TypeScript não faz o mesmo, assim como faz com outros recursos do ES6.

@prashaantt TypeScript não fornece nenhum polyfills e é por design. Se quiser usar Object.assign ou qualquer outro método / primitivo adicionado pelo padrão mais recente, você deve:

  • usar a biblioteca polyfill (como core-js ) com tipificações correspondentes
  • segmente o padrão quando o recurso for adicionado (por exemplo, "target": "es6" em seu tsconfig.json )

@ devoto13 Obrigado, core-js funciona bem.

Eu só tinha uma pergunta boba. Depois de npm install ing e typings install ing core-js , começo a obter o IntelliSense para os métodos polyfilled. Mas ainda vou precisar importar realmente esses métodos em cada módulo onde estou usando-os ou o código compilado não incluiria os polyfills, certo?

@prashaantt Assim que core-js/shim for exigido por qualquer coisa, Object.assign estará disponível globalmente a partir desse ponto. Eu recomendo colocar import 'core-js/shim'; no topo do seu módulo principal / ponto de entrada.

Obrigado @jesseschalken. Como acompanhamento, importar todo o shim vai aumentar meu pacote? Ou tsc ou ts-loader será inteligente o suficiente para pegar apenas as coisas que realmente são usadas em meu código?

@prashaantt Depende dos navegadores que você está Object.assign não é compatível com o navegador que você está alvejando, quem sabe o que mais não é? Você precisa de todo o shim em seu pacote se quiser o mais amplo suporte de navegador.

Se você quiser apenas o polyfill para Object.assign , você pode import 'core-js/modules/es6.object.assign'; e adicionar mais coisas conforme descobrir que precisa delas (consulte shim.js em core-js para uma lista, também os documentos). O Webpack seguirá o gráfico necessário e incluirá apenas os módulos necessários.

Se você já estiver usando o Babel, eu recomendo usar import 'babel-polyfill'; invés de usar core-js diretamente. Inclui core-js/shim mas também regenerator-runtime para geradores / espera assíncrono.

Obrigado pelas dicas, embora devamos ter suporte total aos último obstáculo para 2.0!

Desculpe, não entendo. ES6 suporta Object.assign para que Babel o transpile para um polyfill para destinos ES5. Estou confuso por que o TypeScript não faz o mesmo, assim como faz com outros recursos do ES6.

@prashaantt O que você quer dizer quando diz que Babel _transpiles_ Object.assign ? É apenas uma função. Você pode adicionar polyfill, ponyfill ou escrevê-lo você mesmo, e pode usá-lo em qualquer ambiente - ES 3, 5.1, 6, etc.

@aluanhaddad Meu entendimento do que o babel faz é se você especificar es5 como seu alvo e usar Object.assign , ele inclui automaticamente um polyfill para Object.assign , e não inclui se você não o usar. Seria bom se o typescript fizesse a mesma coisa, já que afirma ser um "superconjunto do es2015", o que não é realmente verdade se não fornecer funcionalidade para transpilar para destinos mais antigos. (Eu posso estar errado embora)

@ devoto13 se seu destino for es5, você deve no mínimo lançar um aviso de que Object.assign não é compatível com es5. Não faz sentido que seja completamente válido e não diga ao programador que você precisa de algum polyfill aleatório.

@kyleholzinger o que você descreveu (almejar ES5 significa que Object.assign não está disponível) já é o comportamento.

@kyleholzinger De fato,

// test.ts
let t = Object.assign({}, {});
// tsconfig.json
{ "target": "es5" }

E então execute tsc nele. Você obterá o seguinte erro:

$ tsc
test.ts(1,16): error TS2339: Property 'assign' does not exist on type 'ObjectConstructor'.

Em seu projeto, você provavelmente inclui tipificações para alguns polyfill, mas não inclui a implementação de polyfill, é por isso que falha.

Eu entendo que é o comportamento atual haha. Meu ponto é que se você especificar o destino como es5, seria bom se o texto digitado fornecesse um erro significativo além de "não está no construtor".

@kyleholzinger FWIW, TypeScript 2.1 agora suporta o ES6 (ES7?) objeto rest / spread então eu pessoalmente acho menos razão para me preocupar com Object.assign mais. Isso com geradores nativos significa que não preciso de polyfills na maioria dos meus projetos.

Isso é verdade. Seria bom não deixar de fora os recursos de linguagem. Seria incrível se Object.assign não dependesse do navegador e o texto digitado avisasse se você não estivesse usando um polyfill.

Se o seu tsconfig estiver definido corretamente, o TypeScript avisa sobre assign não estar disponível para <ES6 por não lhe dar a opção de preencher automaticamente o nome da função em Object em primeiro lugar. Se você persistir em escrever meticulosamente à mão, verá os rabiscos vermelhos. Se você ignorar isso, tsc apresentará o erro acima. Mas se você também ignorar isso deliberadamente, você merece sua condenação por direito. ;)

certo, meu único ponto está nas especificações es2015, então deve ser datilografado;)

Como faço para usar Object.assign no nó enquanto direciono es5 ? Ou seja, o código deve ser executado no servidor e não no navegador. Eu uso polyfills também e como?

@johnbendi
Depende da versão do nó, o que quer dizer que depende do tempo de execução como é o caso com todas as funcionalidades polyfillable.

Aqui está como você pode testar se o seu tempo de execução suporta Object.assign

$ Node 
> Object.assign({ to: 'world' }, { message: 'Hello there!' })
{ to: 'world', message: 'Hello there!' }

Se o procedimento acima funcionar, tudo o que você precisa fazer é incluir "es2017.object" na propriedade "compilerOptions"."lib" do seu tsconfig.json.

Se isso falhar, adicione um polyfill como este, que é escrito em TypeScript.

// polyfill-object-assign.ts

if (typeof Object.assign !== 'function') {
  Object.assign = function (target, ...args) {

    if (!target) {
      throw TypeError('Cannot convert undefined or null to object');
    }
    for (const source of args) {
      if (source) {
        Object.keys(source).forEach(key => target[key] = source[key]);
      }
    }
    return target;
  };
}

E importá-lo com

import './polyfill-object-assign';

E também faça as mesmas alterações em seu tsconfig.json como no caso com suporte de tempo de execução.

Espero que ajude

@aluanhaddad muito obrigado pelos insights. Meu nó suporta Object.assign base no experimento que você me pediu para executar. Mas mesmo depois de adicionar "compilerOptions": { "lib": ["es2017.object"] } , ainda recebo squiggles . Devo simplesmente ignorar ou há algo que posso fazer para fazer isso ir embora.

@aluanhaddad deixa

@aluanhaddad Eu já tinha "compilerOptions": { "lib": ["es2017.object", "es6"] } quando estava recebendo squiggles . A remoção de es6 pareceu resolver, mas executar novamente meu script gulp de repente produz um novo conjunto de erros.
Meu tsconfig.json:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "moduleResolution": "node",
        "lib": ["es2017.object"],
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "sourceMap": true,
        "inlineSources": true,
        //"noImplicitAny": true,
        "declaration": true,
        "noFallthroughCasesInSwitch": true,
        // "noImplicitReturns": true,
        "removeComments": true,
        "stripInternal": true,
        "outDir": "dist"
    },
    "files": ["src/main.ts"],
    "include": [
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

Amostra dos meus novos erros:

error TS2318: Cannot find global type 'Array'.
error TS2318: Cannot find global type 'Boolean'.
error TS2318: Cannot find global type 'Function'.
error TS2318: Cannot find global type 'IArguments'.
error TS2318: Cannot find global type 'Number'.
error TS2318: Cannot find global type 'RegExp'.
error TS2318: Cannot find global type 'String'.
error TS2339: Property 'bind' does not exist on type '(message?: any, ...optionalParams: {}) => void'.
error TS2339: Property 'bind' does not exist on type '(message?: any, ...optionalParams: {}) => void'.
error TS2322: Type '{}' is not assignable to type
error TS2304: Cannot find name 'Promise'.

Então, há uma maneira de usar sua recomendação e ainda obter o texto digitado para compilar corretamente?

@johnbendi sim, com certeza.

Uso Eu estava simplesmente sugerindo adicionar a entrada específica "es2017.object" por causa da especificidade de sua solicitação.
Eu acredito que "lib": ["es6"] não está mais correto e que deveria ser "lib": ["es2015"] .
Experimente "lib": ["es2015", es2017.object"] ou apenas "lib": ["es2017"] .

Acho que o que está faltando é uma maneira limpa de dizer "Eu tenho um polyfill es6 e não é o seu texto datilografado do negócio, apenas suponha que sim" :).

Porque definir target: "es6" faz isso, mas presumivelmente também pode gerar código usando recursos es6 não polyfillable.

A exigência de core-js explicitamente força você a ter o shim, você não pode ter builds com shimmy e não shimmy porque o TS reclamará.

Adicionar node_modules/typescript/lib/lib.es6.d.ts a files em tsconfig.json faz isso, mas .. não parece tão limpo ... (ou estou faltando uma maneira óbvia de fazer isso?)

@himdel apenas use

{
  "compilerOptions": {
    "lib": [
      "es2015"
    ]
  }
}

Funciona perfeitamente - estou fazendo isso há meses.

Ou qualquer subconjunto que você deseja:

{
  "compilerOptions": {
    "lib": [
      "es2015.core",
      "es2016.array.include"
    ]
  }
}

Ainda não consigo descobrir como fazer isso. Quando eu tiver target: "es5" tsc sempre transformará Object.assign em alguma chamada para um polyfill. Adicionar libs não muda nada para mim.

No meu caso, quero que as funções de seta sejam convertidas em funções normais, mas deixe chamadas estáticas como Object.assign e Array.includes intocadas.

tsc deve ter um sinalizador para informá-lo para apenas transpilar recursos de sintaxe e não polyfill recursos como métodos estáticos Object , Array etc.

Quando eu tenho o alvo: "es5" tsc sempre transformará Object.assign em alguma chamada para um polyfill.

@danez TypeScript não modificará uma chamada para Object.assign . Parece que você está executando seu código por meio do Babel?

@RyanCavanaugh Sim . Babel não está envolvida no processo.
O que ele faz é basicamente transformar isso:

var a = Object.assign({}, {});

nisso

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var a = _assign({}, {});

@danez Você pode postar uma reprodução real? O compilador não emitiu esse código

Não tenho um repo, mas este é o tsconfig:

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "module": "ES2015",
    "target": "es5",
    "outDir": "js/lib/es"
  },
  "include": [
    "js/**/*.ts",
  ],
  "exclude": [
    "**/__tests__/**/*.ts"
  ]
}

e então eu simplesmente chamo tsc

@danez , infelizmente, você terá que postar uma reprodução real.

C:\Throwaway\oat>type a.ts
var a = Object.assign({}, {});

C:\Throwaway\oat>type tsconfig.json
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "module": "ES2015",
    "target": "es5",
    "outDir": "js/lib/es"
  },
  "include": [
    "*.ts",
  ]
}
C:\Throwaway\oat>tsc
a.ts(1,16): error TS2339: Property 'assign' does not exist on type 'ObjectConstructor'.

C:\Throwaway\oat>type js\lib\es\a.js
var a = Object.assign({}, {});

@unional porque está funcionando corretamente . O compilador tem um auxiliar para fornecer suporte sintático, mas não _nunca_ reescreve a funcionalidade em globais. @danez, esse auxiliar nem sempre é acessível pelo código TypeScript.

const foo = { foo: 'bar' };
const bar = { ...foo };

irá emitir:

var __assign = (this && this.__assign) || Object.assign || function(t) {
    for (var s, i = 1, n = arguments.length; i < n; i++) {
        s = arguments[i];
        for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
            t[p] = s[p];
    }
    return t;
};
var foo = { foo: 'bar' };
var bar = __assign({}, foo);

Não acho que você possa fornecer um exemplo em que Object.assign() seja reescrito para __assign , ele só é usado no suporte sintático de propagação de objetos.

@kitsonk Sim, eu estava usando propagação de objetos, você está certo. Portanto, seria bom se a propagação do objeto fosse simplesmente transformada em Object.assign

@danez é quando você visa algo que suporta Object.assign() (por exemplo, o destino é es2015 +).

Por exemplo, este:

const foo = { foo: 'bar' };
const bar = { ...foo };

Irá produzir:

const foo = { foo: 'bar' };
const bar = Object.assign({}, foo);

@kitsonk Sim, eu sei, mas estou visando es5 _sintax_ com todos os ES2017 + globais sendo polyfilled por core-js. Portanto, estou me referindo ao que seria bom é um modo que produza a sintaxe es5, mas assume que todos os recursos integrados estão disponíveis. Semelhante ao que o babel está fazendo com a opção useBuiltins : https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx#usebuiltins

Em geral, não nos esforçamos para fornecer suporte especial para destinos de tempo de execução "mistos", mas existem outras opções disponíveis.

Você pode injetar __assign no escopo global (junto com quaisquer outros auxiliares, por exemplo, __extends ) e executar com --noEmitHelpers .

Sim, eu sei, mas estou visando es5 _sintax_

Acho que esse é o principal problema de não aplicar shimming em recursos como Object.assign . Tem que tomar uma posição de uma forma ou de outra - você não pode substituir seu uso por __assign ao usar um spread, mas não quando você chama diretamente, é confuso como o inferno

você não pode substituir seu uso por __assign ao usar um spread, mas não quando você chama diretamente, é confuso como o inferno

Eu posso entender que você acha que é confuso, mas não é se você mantiver o mantra, o TypeScript fornece _reescritas sintáticas_ e não _polifills funcionais_. O TypeScript é totalmente consistente em como se comporta e é opinativo sobre o que faz e 99% das vezes não tem impacto no usuário final.

Como @RyanCavanaugh diz, é possível alavancar um conjunto de polyfills, mais tslib , com --noEmitHelpers mais um script global que diz:

__assign = Object.assign;

Mas isso é realmente uma "rodada de bônus" do TypeScript e pode-se argumentar que forneceria qualquer melhoria de desempenho mensurável no mundo real.

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

Questões relacionadas

jbondc picture jbondc  ·  3Comentários

Antony-Jones picture Antony-Jones  ·  3Comentários

CyrusNajmabadi picture CyrusNajmabadi  ·  3Comentários

dlaberge picture dlaberge  ·  3Comentários

seanzer picture seanzer  ·  3Comentários