Typescript: ¿Alguna hoja de ruta sobre el apoyo a Object.assign?

Creado en 9 jun. 2015  ·  47Comentarios  ·  Fuente: microsoft/TypeScript

en 1.6 o 2.0?

Question

Comentario más útil

Lo siento, no entiendo. ES6 admite Object.assign por lo que Babel lo transpila a un polyfill para los objetivos de ES5. Estoy confundido por qué TypeScript no hace lo mismo, tal como lo hace con otras funciones de ES6.

Todos 47 comentarios

No estás seguro de lo que buscas. si te refieres a los mecanografiados, ya está definido en 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;

es decir, la implementación, TypeScript no incluye pollyfills, siempre puede incluir el suyo, por ejemplo, Mozilla tiene un pollyfill para ello.

Si está preguntando acerca de la tipificación del patrón de mezcla, es algo que siempre tuvimos en la hoja de ruta para 2.0.

Gracias. Me refiero a escribir Object.assign(...) en TypeScript que se transpile a es3 / es5 / es6. Así que está en la hoja de ruta para 2.0.

: +1:

@unional Puede que haya entendido mal algo. Pero no creo que tu conclusión sea correcta.

ES6 de todos modos admite Object.assign , por lo que si su objetivo es ES6, debería poder escribirlo en TypeScript de todos modos.

Pero si transpila a es5 e inferior, aún necesitará un polyfill, porque es5 no tiene Object.assign .

Como solución rápida, considere este polyfill y escriba:

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

Gracias por investigar este problema. Fue hace bastante tiempo. :) Sí, ahora entiendo que simplemente requiere un polyfill para es5.

¡Gracias de nuevo!

¿Compilaremos este polyfill cuando target se establezca en es5? Quiero decir, dado que el objetivo de compilación está configurado en es5, entonces esto debería poder funcionar en el entorno es5.

¿Compilaremos este polyfill cuando el objetivo se establezca en es5?

Sí, como relleno de caballo: rosa:: caballo:

Lo siento, no entiendo. ES6 admite Object.assign por lo que Babel lo transpila a un polyfill para los objetivos de ES5. Estoy confundido por qué TypeScript no hace lo mismo, tal como lo hace con otras funciones de ES6.

@prashaantt TypeScript no proporciona ningún polyfills y es por diseño. Si desea usar Object.assign o cualquier otro método / primitivo agregado por un estándar más nuevo, debe:

  • use la biblioteca polyfill (como core-js ) con las tipificaciones correspondientes
  • apuntar al estándar cuando se agregó la función (por ejemplo, "target": "es6" en su tsconfig.json )

@ devoto13 Gracias, core-js funciona bien.

Solo tenía una pregunta tonta. Después de npm install ing y typings install ing core-js empiezo a obtener IntelliSense para los métodos con relleno múltiple. Pero todavía necesitaré importar esos métodos en cada módulo donde los esté usando o el código compilado no incluiría los polyfills, ¿verdad?

@prashaantt Una vez que algo core-js/shim , Object.assign estará disponible globalmente a partir de ese momento. Recomiendo poner import 'core-js/shim'; en la parte superior de su módulo principal / punto de entrada.

Gracias @jesseschalken. Como seguimiento, ¿importar el shim completo no aumentará mi paquete? ¿O tsc o ts-loader serán lo suficientemente inteligentes como para recoger solo las cosas que realmente se usan en mi código?

@prashaantt Depende de los navegadores a los que se dirija. Ya sabes que Object.assign no es compatible con un navegador al que te diriges, ¿quién sabe qué más no lo es? Necesita la calza completa en su paquete si desea la mayor compatibilidad con el navegador.

Si solo desea el polyfill por Object.assign , puede import 'core-js/modules/es6.object.assign'; y agregar más cosas a medida que descubra que las necesita (consulte shim.js en core-js para obtener una lista, también los documentos). Webpack seguirá el gráfico requerido y solo incluirá los módulos necesarios.

Si ya está usando Babel, le recomiendo usar import 'babel-polyfill'; lugar de usar core-js directamente. Incluye core-js/shim pero también regenerator-runtime para generadores / async-await.

Gracias por los consejos, aunque deberíamos tener un soporte completo de último obstáculo para 2.0!

Lo siento, no entiendo. ES6 admite Object.assign, por lo que Babel lo transpila en un polyfill para los objetivos de ES5. Estoy confundido por qué TypeScript no hace lo mismo, tal como lo hace con otras funciones de ES6.

@prashaantt ¿A qué te refieres cuando dices que Babel _transpiles_ Object.assign ? Es solo una función. Puede agregar el polyfill, ponyfill o escribirlo usted mismo, y puede usarlo en cualquier entorno: ES 3, 5.1, 6, etc.

@aluanhaddad Mi comprensión de lo que hace babel es que si especificas es5 como tu objetivo y usas Object.assign , automáticamente incluye un polyfill por Object.assign , y no lo hace si no lo usas. Sería bueno si mecanografiado hiciera lo mismo, ya que se afirma que es un "superconjunto de es2015", lo cual no es realmente cierto si no proporciona funcionalidad para transpilar a objetivos más antiguos. (Aunque podría estar equivocado)

@ devoto13 si tu objetivo es es5, al menos deberías lanzar una advertencia de que Object.assign no es compatible con es5. No tiene ningún sentido que sea completamente válido y no le diga al programador que necesita un polyfill aleatorio.

@kyleholzinger lo que ha descrito (apuntar a ES5 significa que Object.assign no está disponible) ya es el comportamiento.

@kyleholzinger De hecho, arroja un error. Si crea una carpeta con dos archivos siguientes:

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

Y luego ejecute tsc en él. Obtendrá el siguiente error:

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

En su proyecto probablemente incluya mecanografía para algún polyfill, pero no incluya la implementación de polyfill, por eso falla.

Entiendo que es el comportamiento actual jaja. Mi punto es que si especificas el objetivo como es5, sería bueno si el mecanografiado te diera un error significativo más allá de "no está en el constructor".

@kyleholzinger FWIW, TypeScript 2.1 ahora admite el reposo / propagación del objeto ES6 (ES7?), por lo que personalmente encuentro una razón menor para preocuparme por Object.assign . Eso con generadores nativos significa que no necesito polyfills en la mayoría de mis proyectos.

Es verdad. Sería bueno no dejar de lado las características del idioma. Sería increíble si Object.assign no dependiera del navegador y el mecanografiado le advirtiera si no estuviera usando un polyfill.

Si su tsconfig está configurado correctamente, TypeScript le advierte que assign no estará disponible para <ES6 al no darle la opción de autocompletar ese nombre de función en Object en primer lugar. Si persiste en escribirlo cuidadosamente a mano, verá los garabatos rojos. Si ignora eso, tsc le dará el error anterior. Pero si también ignoras eso intencionalmente, mereces tu perdición. ;)

correcto, mi único punto está en la especificación es2015, por lo que debería estar mecanografiado;)

¿Cómo uso Object.assign en el nodo mientras apunto es5 ? Es decir, el código debe ejecutarse en el servidor y no en el navegador. ¿También uso polyfills y cómo?

@johnbendi
Depende de la versión del nodo, lo que quiere decir que depende del tiempo de ejecución, como es el caso de todas las funciones polifillables.

Así es como puede probar si su tiempo de ejecución admite Object.assign

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

Si lo anterior funciona, todo lo que necesita hacer es incluir "es2017.object" en la propiedad "compilerOptions"."lib" de su tsconfig.json.

Si lo anterior falla, agregue un polyfill como este, que está escrito en 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 importarlo con

import './polyfill-object-assign';

Y también realice los mismos cambios en su tsconfig.json que para el caso compatible con el tiempo de ejecución.

Espero que eso ayude

@aluanhaddad muchas gracias por las ideas. Mi nodo admite Object.assign según el experimento que me pidió que ejecutara. Pero incluso después de agregar el "compilerOptions": { "lib": ["es2017.object"] } todavía obtengo el squiggles . ¿Debo simplemente ignorarlo o hay algo que pueda hacer para que desaparezca?

@aluanhaddad no importa. Funciona bien ahora.

@aluanhaddad Anteriormente tenía "compilerOptions": { "lib": ["es2017.object", "es6"] } cuando recibía el squiggles . La eliminación de es6 pareció resolverse, pero volver a ejecutar mi script gulp de repente produce un nuevo conjunto de errores.
Mi 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"
    ]
}

Muestra de mis nuevos errores:

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'.

Entonces, ¿hay alguna manera de usar su recomendación y aún así obtener mecanografiado para compilar correctamente?

@johnbendi sí, ciertamente.

Utilice Simplemente estaba sugiriendo agregar la entrada específica "es2017.object" debido a la especificidad de su solicitud.
Creo que "lib": ["es6"] ya no es correcto y debería ser "lib": ["es2015"] .
Pruebe "lib": ["es2015", es2017.object"] o simplemente "lib": ["es2017"] .

Creo que lo que falta es una forma clara de decir "Tengo un polyfill es6 y no es el texto mecanografiado de su empresa, asuma que sí" :)

Porque, configurar target: "es6" hace eso, pero presumiblemente también puede generar código usando características es6 no polifilables.

Requerir core-js explícitamente esencialmente lo obliga a tener el shim, no puede tener compilaciones shimmy y no shimmy porque TS se quejará.

Agregar node_modules/typescript/lib/lib.es6.d.ts a files en tsconfig.json hace eso, pero ... no se ve tan limpio ... (¿o me estoy perdiendo una forma obvia de lograrlo?)

@himdel solo usa

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

Funciona perfectamente, lo he estado haciendo durante meses.

O cualquier subconjunto que desee:

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

Sigo sin saber cómo hacer esto. Cuando tengo target: "es5" tsc siempre transformará Object.assign en alguna llamada a un polyfill. Agregar libs no cambia nada para mí.

En mi caso, quiero que las funciones de flecha se conviertan en funciones normales, pero dejo las llamadas estáticas como Object.assign y Array.includes sin tocar.

tsc debería tener una bandera para indicarle que solo transpile características de sintaxis y no características de polyfill como métodos estáticos Object , Array etc.

Cuando tengo target: "es5" tsc siempre transformará Object.assign en alguna llamada a un polyfill.

@danez TypeScript no modificará una llamada a Object.assign . ¿Parece que está ejecutando su código a través de Babel?

@RyanCavanaugh Lo hace. Babel no participa en el proceso.
Lo que hace es básicamente transformar esto:

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

dentro de esto

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 ¿Puedes publicar una reproducción real? El compilador no emitió ese código

No tengo un repositorio, pero este es el tsconfig:

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

y luego simplemente llamo tsc

@danez Me temo que tendrás que publicar una reproducción 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 correctamente . El compilador tiene un ayudante para proporcionar soporte sintáctico, pero no _ever_ reescribe la funcionalidad en globales. @danez nunca se puede

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

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

No creo que pueda proporcionar un ejemplo en el que Object.assign() se reescriba a __assign , solo se usa en el soporte sintáctico de propagación de objetos.

@kitsonk Sí, estaba usando la extensión de objetos, tienes razón. Por lo tanto, sería bueno si la extensión del objeto simplemente se transformara en Object.assign

@danez es cuando Object.assign() (por ejemplo, el objetivo es es2015 +).

Por ejemplo, esto:

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

Saldrá:

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

@kitsonk Sí, lo sé, pero estoy apuntando a es5 _syntax_ con todos los globales de ES2017 + que están rellenados por core-js. Entonces, a lo que me refiero a lo que sería bueno es a un modo que genera la sintaxis es5 pero asume que todas las funciones internas están disponibles. Similar a lo que está haciendo babel con la opción useBuiltins : https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx#usebuiltins

En general, no nos esforzamos por brindar soporte especial a objetivos de tiempo de ejecución "mixtos", pero hay otras opciones disponibles.

Puede inyectar __assign en el alcance global (junto con otros ayudantes, por ejemplo, __extends ) y ejecutar con --noEmitHelpers .

Sí, lo sé, pero me dirijo a es5 _syntax_

Siento que este es el principal problema de no ajustar funciones como Object.assign . Tengo que adoptar una postura de una forma u otra: no puede reemplazar su uso con __assign cuando usa una extensión, pero no cuando lo llama directamente, es muy confuso

no puede reemplazar su uso con __assign cuando usa una extensión, pero no cuando lo llama directamente, es confuso como el infierno

Puedo entender que sienta que es confuso, pero no lo es si mantiene el mantra, TypeScript proporciona _ reescrituras sintéticas_ no _ polyfills funcionales_. TypeScript es totalmente coherente en su comportamiento y tiene opiniones sobre lo que hace, y el 99% de las veces no tiene ningún impacto en el usuario final.

Como dice @RyanCavanaugh , es posible aprovechar un conjunto de polyfills, más tslib , con --noEmitHelpers más un script global que dice:

__assign = Object.assign;

Pero eso es realmente TypeScript de "ronda de bonificación" y se puede argumentar que proporcionaría una mejora de rendimiento medible en el mundo real.

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

Temas relacionados

blendsdk picture blendsdk  ·  3Comentarios

wmaurer picture wmaurer  ·  3Comentarios

jbondc picture jbondc  ·  3Comentarios

bgrieder picture bgrieder  ·  3Comentarios

MartynasZilinskas picture MartynasZilinskas  ·  3Comentarios