Typescript: Une feuille de route sur la prise en charge d'Object.assign?

Créé le 9 juin 2015  ·  47Commentaires  ·  Source: microsoft/TypeScript

en 1.6 ou 2.0?

Question

Commentaire le plus utile

Je suis désolé de ne pas comprendre. ES6 supporte Object.assign donc Babel le transpile en polyfill pour les cibles ES5. Je ne comprends pas pourquoi TypeScript ne fait pas la même chose, tout comme il le fait pour les autres fonctionnalités ES6.

Tous les 47 commentaires

Pas sûr de ce que vous cherchez. si vous voulez dire les typages, il est déjà défini dans lib.es6.d.ts comme

    /**
      * 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;

je veux dire l'implémentation, TypeScript n'inclut pas les pollyfills, vous pouvez toujours inclure les vôtres, par exemple Mozilla a un pollyfill pour cela.

Si vous posez des questions sur les typages de motifs de mixin, c'est quelque chose que nous avons toujours eu sur la feuille de route pour 2.0.

Merci. Je veux dire écrire Object.assign(...) dans TypeScript qui transpile en es3 / es5 / es6. C'est donc sur la feuille de route pour 2.0.

: +1:

@unional J'ai peut-être mal compris quelque chose. Mais je ne pense pas que votre conclusion soit correcte.

ES6 supporte de toute façon Object.assign , donc si votre cible est ES6, vous devriez quand même pouvoir l'écrire en TypeScript.

Mais si vous transpilez vers es5 et ci-dessous, vous aurez toujours besoin d'un polyfill, car es5 n'a pas Object.assign .

Comme solution de contournement rapide, considérez ce polyfill et la saisie:

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

Merci d'avoir déterré ce problème. C'était il y a pas mal de temps. :) Oui, je comprends maintenant qu'il nécessite simplement un polyfill pour es5.

Merci encore!

Compilerons-nous ce polyfill lorsque la cible est définie sur es5? Je veux dire, puisque la cible de compilation est définie sur es5, cela devrait pouvoir fonctionner sous l'environnement es5.

Compilerons-nous ce polyfill lorsque la cible est définie sur es5

Oui, en peluche: rose:: cheval:

Je suis désolé de ne pas comprendre. ES6 supporte Object.assign donc Babel le transpile en polyfill pour les cibles ES5. Je ne comprends pas pourquoi TypeScript ne fait pas la même chose, tout comme il le fait pour les autres fonctionnalités ES6.

@prashaantt TypeScript ne fournit aucun polyfill et c'est par conception. Si vous souhaitez utiliser Object.assign ou toute autre primitive / méthode ajoutée par une norme plus récente, vous devez soit:

  • utiliser la bibliothèque polyfill (comme core-js ) avec les typages correspondants
  • ciblez le standard lorsque la fonctionnalité a été ajoutée (par exemple "target": "es6" dans votre tsconfig.json )

@ devoto13 Merci, core-js fonctionne bien.

J'ai juste eu une question idiote. Après npm install ing et typings install ing core-js je commence à obtenir IntelliSense pour les méthodes polyfilled. Mais je devrai toujours importer ces méthodes dans chaque module où je les utilise, sinon le code compilé n'inclurait pas les polyfills, non?

@prashaantt Une fois que core-js/shim est requis par quoi que ce soit, Object.assign sera disponible dans le monde entier à partir de ce moment. Je recommande de mettre import 'core-js/shim'; en haut de votre module principal / point d'entrée.

Merci @jesseschalken. En guise de suivi, l'importation de l'ensemble de shim va-t-elle pas gonfler mon bundle? Ou est-ce que tsc ou ts-loader sera assez intelligent pour ne prendre en compte que les éléments qui sont réellement utilisés dans mon code?

@prashaantt Cela dépend des navigateurs que vous ciblez. Vous savez déjà que Object.assign n'est pas pris en charge par le navigateur que vous ciblez, qui sait quoi d'autre ne l'est pas? Vous avez besoin du shim entier dans votre bundle si vous voulez la prise en charge la plus large du navigateur.

Si vous voulez seulement le polyfill pour Object.assign , vous pouvez import 'core-js/modules/es6.object.assign'; , et ajouter plus de choses lorsque vous découvrez que vous en avez besoin (voir shim.js dans core-js pour une liste, aussi les docs). Webpack suivra le graphique requis et n'inclura que les modules nécessaires.

Si vous utilisez déjà Babel, je vous recommande d'utiliser import 'babel-polyfill'; au lieu d'utiliser directement core-js. Il inclut core-js/shim mais aussi regenerator-runtime pour les générateurs / async-await.

Merci pour les conseils, même si nous devrions avoir des générateurs complets à tout moment maintenant - littéralement le dernier obstacle à 2.0!

Je suis désolé de ne pas comprendre. ES6 prend en charge Object.assign afin que Babel le transpile en polyfill pour les cibles ES5. Je ne comprends pas pourquoi TypeScript ne fait pas la même chose, tout comme il le fait pour les autres fonctionnalités ES6.

@prashaantt Que voulez-vous dire quand vous dites que Babel _transpiles_ Object.assign ? C'est juste une fonction. Vous pouvez ajouter le polyfill, ponyfill ou l'écrire vous-même, et vous pouvez l'utiliser dans n'importe quel environnement - ES 3, 5.1, 6, etc.

@aluanhaddad Ma compréhension de ce que fait babel est que si vous spécifiez es5 comme cible et utilisez Object.assign , il inclut automatiquement un polyfill pour Object.assign , et ne le fait pas si vous ne l'utilisez pas. Ce serait bien si dactylographié faisait la même chose, car il prétend que c'est un "sur-ensemble d'es2015", ce qui n'est pas vraiment vrai s'il ne fournit pas de fonctionnalités pour transpiler vers des cibles plus anciennes. (Je pourrais toutefois avoir tord)

@ devoto13 si votre cible est es5, alors vous devriez au moins lancer un avertissement indiquant qu'Object.assign n'est pas pris en charge dans es5. Cela n'a aucun sens de l'avoir complètement valide et de ne pas dire au programmeur que vous avez besoin d'un polyfill aléatoire.

@kyleholzinger ce que vous avez décrit (cibler ES5 signifie qu'Object.assign n'est pas disponible) est déjà le comportement.

@kyleholzinger Cela

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

Et puis exécutez tsc dessus. Vous obtiendrez l'erreur suivante:

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

Dans votre projet, vous incluez probablement des typages pour certains polyfill, mais n'incluez pas l'implémentation de polyfill, c'est pourquoi cela échoue.

Je comprends que c'est le comportement actuel haha. Mon point est que si vous spécifiez la cible comme es5, ce serait bien si dactylographié vous donnait une erreur significative au-delà de "ce n'est pas sur le constructeur".

@kyleholzinger FWIW, TypeScript 2.1 prend désormais en charge le reste / propagation de l'objet ES6 (ES7?), donc je trouve personnellement moins de raisons de me soucier de Object.assign . Cela avec les générateurs natifs signifie que je n'ai pas besoin de polyfills dans la plupart de mes projets.

C'est vrai. Ce serait juste bien de ne pas laisser de côté les fonctionnalités linguistiques. Ce serait génial si Object.assign n'était pas dépendant du navigateur et dactylographié vous prévenait si vous n'utilisiez pas de polyfill.

Si votre tsconfig est réglé correctement, TypeScript vous avertit que assign n'est pas disponible pour <ES6 en ne vous donnant pas la possibilité de compléter automatiquement ce nom de fonction sur Object en premier lieu. Si vous persistez à l'écrire minutieusement à la main, vous verrez les gribouillis rouges. Si vous ignorez cela, tsc vous donnera l'erreur ci-dessus. Mais si vous l'ignorez volontairement aussi, vous méritez à juste titre votre destin. ;)

à droite, mon seul point est dans la spécification es2015, donc ça devrait être en tapuscrit;)

Comment utiliser Object.assign sur un nœud tout en ciblant es5 ? Autrement dit, le code doit s'exécuter sur le serveur et non dans le navigateur. Dois-je aussi utiliser des polyfills et comment?

@johnbendi
Cela dépend de la version du node, c'est-à-dire qu'il dépend du runtime comme c'est le cas pour toutes les fonctionnalités polyfillables.

Voici comment tester si votre runtime prend en charge Object.assign

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

Si cela fonctionne, tout ce que vous avez à faire est d'inclure "es2017.object" dans la propriété "compilerOptions"."lib" de votre tsconfig.json.

Si cela échoue, ajoutez un polyfill comme celui-ci, qui est écrit 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;
  };
}

Et importez-le avec

import './polyfill-object-assign';

Et apportez également les mêmes modifications à votre tsconfig.json que pour le cas pris en charge à l'exécution.

J'espère que ça aide

@aluanhaddad merci beaucoup pour les idées. Mon nœud prend en charge Object.assign fonction de l'expérience que vous m'avez demandé d'exécuter. Mais même après avoir ajouté le "compilerOptions": { "lib": ["es2017.object"] } j'obtiens toujours le squiggles . Dois-je simplement l'ignorer ou y a-t-il quelque chose que je puisse faire pour le faire disparaître?

@aluanhaddad, peu importe. Cela fonctionne bien maintenant.

@aluanhaddad J'avais auparavant "compilerOptions": { "lib": ["es2017.object", "es6"] } quand j'obtenais le squiggles . La suppression de es6 semblé résoudre mais la réexécution de mon script gulp produit soudainement un nouvel ensemble d'erreurs.
Mon 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"
    ]
}

Exemple de mes nouvelles erreurs:

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

Alors, y a-t-il un moyen d'utiliser votre recommandation tout en obtenant du dactylographie pour compiler correctement?

@johnbendi oui, certainement.

Utilisation Je suggérais simplement d'ajouter l'entrée spécifique "es2017.object" raison de la spécificité de votre demande.
Je crois que "lib": ["es6"] n'est plus correct et qu'il devrait être "lib": ["es2015"] .
Essayez "lib": ["es2015", es2017.object"] ou juste "lib": ["es2017"] .

Je pense que ce qui manque, c'est une façon propre de dire "J'ai un polyfill es6 et ce n'est pas votre script dactylographié, supposez simplement que je le fais" :).

Parce que définir target: "es6" fait cela, mais peut probablement aussi générer du code en utilisant des fonctionnalités es6 non polyfillables.

Exiger explicitement core-js vous oblige essentiellement à avoir le shim, vous ne pouvez pas avoir de builds shimmy et non shimmy car TS se plaindra.

Ajouter node_modules/typescript/lib/lib.es6.d.ts à files dans tsconfig.json fait cela, mais .. n'a pas l'air si propre ... (ou est-ce que je manque un moyen évident d'y parvenir?)

@himdel utilise juste

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

Cela fonctionne parfaitement - je le fais depuis des mois.

Ou tout sous-ensemble que vous souhaitez:

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

Je n'arrive toujours pas à comprendre comment faire cela. Quand j'ai target: "es5" tsc transformera toujours Object.assign en un appel à un polyfill. L'ajout de bibliothèques ne change rien du tout pour moi.

Dans mon cas, je veux que les fonctions fléchées soient converties en fonctions normales, mais laissez les appels statiques comme Object.assign et Array.includes inchangés.

tsc devrait avoir un indicateur pour lui indiquer uniquement les fonctionnalités de syntaxe transpile et non les fonctionnalités de polyfill comme les méthodes statiques Object , Array etc.

Quand j'ai la cible: "es5" tsc transformera toujours Object.assign en un appel à un polyfill.

@danez TypeScript ne modifiera pas un appel à Object.assign . Il semble que vous exécutiez votre code via Babel?

@RyanCavanaugh C'est
Ce qu'il fait, c'est fondamentalement transformer ceci:

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

dans ce

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 Pouvez-vous publier une véritable repro? Le compilateur n'a pas émis ce code

Je n'ai pas de dépôt, mais voici le tsconfig:

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

puis j'appelle simplement tsc

@danez J'ai peur que vous deviez poster une véritable repro.

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 en ce qu'il fonctionne correctement . Le compilateur a une aide pour fournir un support syntaxique, mais il ne réécrit pas jamais la fonctionnalité sur les globaux. @danez que helper n'est jamais accessible par le code TypeScript.

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

émettra:

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

Je ne pense pas que vous puissiez fournir un exemple où Object.assign() est réécrit en __assign , il n'est utilisé que dans le support syntaxique de propagation d'objets.

@kitsonk Oui, j'utilisais la propagation d'objets, vous avez raison. Ce serait bien si la propagation d'objet était simplement transformée en Object.assign

@danez c'est lorsque vous ciblez quelque chose qui supporte Object.assign() (par exemple, la cible est es2015 +).

Par exemple, ceci:

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

Sortira:

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

@kitsonk Oui, je sais, mais je cible es5 _syntax_ avec tous les globaux ES2017 + polyfilled par core-js. Donc, ce que je veux dire, ce qui serait bien, c'est un mode qui génère la syntaxe es5 mais suppose que toutes les fonctions intégrées sont disponibles. Similaire à ce que fait babel avec l'option useBuiltins : https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx#usebuiltins

En général, nous ne faisons pas tout notre possible pour apporter un support spécial aux cibles d'exécution "mixtes", mais il existe d'autres options disponibles.

Vous pouvez injecter __assign dans la portée globale (avec tous les autres helpers, par exemple __extends ) et exécuter avec --noEmitHelpers .

Oui, je sais, mais je cible es5 _syntax_

J'ai l'impression que c'est le principal problème de ne pas modifier des fonctionnalités telles que Object.assign . Je dois prendre une position d'une manière ou d'une autre - vous ne pouvez pas remplacer son utilisation par __assign lorsque vous utilisez un spread mais pas lorsque vous l'appelez directement, c'est déroutant.

vous ne pouvez pas remplacer son utilisation par __assign lors de l'utilisation d'un spread mais pas lorsque vous l'appelez directement, c'est déroutant comme l'enfer

Je peux comprendre que vous pensez que c'est déroutant, mais ce n'est pas le cas si vous gardez le mantra, TypeScript fournit des _ réécritures synchroniques_ et non des polyfills fonctionnels_. TypeScript est totalement cohérent dans son comportement, et il est avisé de ce qu'il fait, et 99% du temps, il n'a aucun impact sur l'utilisateur final.

Comme le dit @RyanCavanaugh , il est possible de tirer parti d'un ensemble de polyfills, plus tslib , avec --noEmitHelpers plus un script global qui dit:

__assign = Object.assign;

Mais c'est vraiment du TypeScript "bonus round" et on peut soutenir qu'il fournirait n'importe quelle amélioration de performance mesurable dans le monde réel.

Cette page vous a été utile?
0 / 5 - 0 notes