Typescript: Gibt es eine Roadmap zur Unterstützung von Object.assign?

Erstellt am 9. Juni 2015  ·  47Kommentare  ·  Quelle: microsoft/TypeScript

in 1.6 oder 2.0?

Question

Hilfreichster Kommentar

Es tut mir leid, dass ich nicht verstehe. ES6 unterstützt Object.assign sodass Babel es in eine Polyfüllung für ES5-Ziele umwandelt. Ich bin verwirrt, warum TypeScript nicht dasselbe tut wie bei anderen ES6-Funktionen.

Alle 47 Kommentare

Ich bin mir nicht sicher, wonach du suchst. Wenn Sie die Typisierungen meinen, ist sie bereits in lib.es6.d.ts als definiert

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

Wenn Sie die Implementierung meinen, enthält TypeScript keine Pollyfills, Sie können immer Ihre einschließen, zum Beispiel hat Mozilla eine Pollyfill dafür.

Wenn Sie nach den Typisierungen der Mixin-Muster fragen, war dies etwas, das wir immer auf der Roadmap für 2.0 hatten.

Vielen Dank. Ich meine, Object.assign(...) in TypeScript zu schreiben, das in es3 / es5 / es6 transpiliert wird. So steht es auf der Roadmap für 2.0.

: +1:

@unional Ich habe vielleicht etwas falsch verstanden. Aber ich denke nicht, dass Ihre Schlussfolgerung richtig ist.

ES6 unterstützt sowieso Object.assign . Wenn Ihr Ziel also ES6 ist, sollten Sie es trotzdem in TypeScript schreiben können.

Wenn Sie jedoch auf es5 und darunter transpilieren, benötigen Sie immer noch eine Polyfüllung, da es5 nicht über Object.assign .

Betrachten Sie als schnelle Problemumgehung diese Polyfüllung und Eingabe:

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

Vielen Dank, dass Sie dieses Problem behoben haben. Es ist schon eine ganze Weile her. :) Ja, ich verstehe jetzt, dass für es5 einfach eine Polyfüllung erforderlich ist.

Danke noch einmal!

Werden wir diese Polyfüllung kompilieren, wenn das Ziel auf es5 gesetzt ist? Ich meine, da das Kompilierungsziel auf es5 festgelegt ist, sollte dies in der es5-Umgebung funktionieren können.

Werden wir diese Polyfüllung kompilieren, wenn das Ziel auf es5 gesetzt ist?

Ja, als Ponyfüllung: Rose :: Pferd:

Es tut mir leid, dass ich nicht verstehe. ES6 unterstützt Object.assign sodass Babel es in eine Polyfüllung für ES5-Ziele umwandelt. Ich bin verwirrt, warum TypeScript nicht dasselbe tut wie bei anderen ES6-Funktionen.

@prashaantt TypeScript bietet keine Polyfills und ist Object.assign oder ein anderes Primitiv / eine andere Methode verwenden möchten, die durch einen neueren Standard hinzugefügt wurde, sollten Sie entweder:

  • Verwenden Sie die Polyfill-Bibliothek (wie core-js ) mit entsprechenden Typisierungen
  • zielen Sie auf den Standard ab, als die Funktion hinzugefügt wurde (z. B. "target": "es6" in Ihrem tsconfig.json )

@ devoto13 Danke, core-js funktioniert gut.

Ich hatte gerade eine dumme Frage. Nach npm install ing und typings install ing core-js bekomme ich IntelliSense für die polygefüllten Methoden. Aber ich muss diese Methoden trotzdem in jedes Modul importieren, in dem ich sie verwende, oder der kompilierte Code würde die Polyfills nicht enthalten, oder?

@prashaantt Sobald core-js/shim von irgendetwas benötigt wird, ist Object.assign ab diesem Zeitpunkt global verfügbar. Ich empfehle, import 'core-js/shim'; oben auf Ihrem Hauptmodul / Einstiegspunkt zu platzieren.

Danke @jesseschalken. Wird der Import des gesamten shim nicht mein Bundle aufblähen? Oder wird tsc oder ts-loader klug genug sein, um nur die Dinge aufzunehmen, die tatsächlich in meinem Code verwendet werden?

@prashaantt Es hängt von den Browsern ab, auf die Sie abzielen. Sie wissen bereits, dass Object.assign von einem Browser, auf den Sie abzielen, nicht unterstützt wird. Wer weiß, was nicht? Sie benötigen den gesamten Shim in Ihrem Bundle, wenn Sie die breiteste Browserunterstützung wünschen.

Wenn Sie die Polyfüllung nur für Object.assign möchten, können Sie import 'core-js/modules/es6.object.assign'; und weitere Dinge hinzufügen, wenn Sie feststellen, dass Sie sie benötigen (eine Liste finden Sie auch unter shim.js in core-js die Dokumente). Webpack folgt dem erforderlichen Diagramm und enthält nur die erforderlichen Module.

Wenn Sie Babel bereits verwenden, empfehle ich die Verwendung von import 'babel-polyfill'; anstelle von core-js direkt. Es enthält core-js/shim aber auch regenerator-runtime für Generatoren / Async-Warten.

Vielen Dank für die Tipps, obwohl wir jetzt jeden Moment volle Generatorunterstützung haben sollten - buchstäblich die letzte Hürde für 2.0!

Es tut mir leid, dass ich nicht verstehe. ES6 unterstützt Object.assign, sodass Babel es in eine Polyfüllung für ES5-Ziele umwandelt. Ich bin verwirrt, warum TypeScript nicht dasselbe tut wie bei anderen ES6-Funktionen.

@prashaantt Was meinst du, wenn du sagst, dass Babel _transpiles_ Object.assign ? Es ist nur eine Funktion. Sie können die Polyfill, Ponyfill hinzufügen oder selbst schreiben und sie in jeder Umgebung verwenden - ES 3, 5.1, 6 usw.

@aluanhaddad Ich verstehe, was babel tut, wenn Sie es5 als Ziel angeben und Object.assign . Es enthält automatisch eine Polyfüllung für Object.assign und nicht, wenn Sie es nicht verwenden. Es wäre schön, wenn Typoskript dasselbe tun würde, da behauptet wird, es sei eine "Obermenge von es2015", was nicht wirklich zutrifft, wenn es keine Funktionalität zum Transpilieren auf ältere Ziele bietet. (Ich könnte mich jedoch irren)

@ devoto13 Wenn Ihr Ziel es5 ist, sollten Sie zumindest eine Warnung auslösen, dass Object.assign in es5 nicht unterstützt wird. Es macht keinen Sinn, dass es vollständig gültig ist und dem Programmierer nicht sagt, dass Sie eine zufällige Polyfüllung benötigen.

@kyleholzinger Was Sie beschrieben haben (das Targeting von ES5 bedeutet, dass Object.assign nicht verfügbar ist), ist bereits das Verhalten.

@kyleholzinger Es wirft tatsächlich Fehler. Wenn Sie einen Ordner mit zwei folgenden Dateien erstellen:

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

Und dann tsc darauf ausführen. Sie erhalten folgenden Fehler:

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

In Ihrem Projekt enthalten Sie wahrscheinlich Typisierungen für einige Polyfills, aber keine Polyfill-Implementierung. Deshalb schlägt dies fehl.

Ich verstehe, dass es das aktuelle Verhalten ist, haha. Mein Punkt ist, dass, wenn Sie das Ziel als es5 angeben, es schön wäre, wenn Typoskript Ihnen einen aussagekräftigen Fehler über "es ist nicht auf dem Konstruktor" gab.

@kyleholzinger FWIW, TypeScript 2.1 unterstützt jetzt das ES6 (ES7?) - Objekt Rest / Spread, sodass ich persönlich weniger Grund finde, mich mehr um Object.assign kümmern. Das bedeutet, dass ich mit nativen Generatoren in den meisten meiner Projekte keine Polyfills benötige.

Das stimmt. Es wäre einfach schön, Sprachfunktionen nicht auszulassen. Es wäre fantastisch, wenn Object.assign nicht browserabhängig wäre und Typoskript Sie warnt, wenn Sie keine Polyfüllung verwenden.

Wenn Ihre tsconfig richtig eingestellt ist, warnt TypeScript Sie davor, dass assign für <ES6 nicht verfügbar ist, indem Sie nicht die Option erhalten, diesen Funktionsnamen für Object automatisch zu vervollständigen. Wenn Sie es weiterhin sorgfältig von Hand ausschreiben, sehen Sie die roten Kringel. Wenn Sie dies ignorieren, gibt tsc den obigen Fehler aus. Aber wenn Sie das auch absichtlich ignorieren, verdienen Sie zu Recht Ihr Schicksal. ;)

Richtig, mein einziger Punkt ist in der es2015-Spezifikation, also sollte es in Typoskript sein;)

Wie verwende ich Object.assign auf dem Knoten, während ich auf es5 ziele? Das heißt, Code soll auf dem Server und nicht im Browser ausgeführt werden. Benutze ich auch Polyfills und wie?

@ Johnbendi
Dies hängt von der Version des Knotens ab, dh es hängt von der Laufzeit ab, wie dies bei allen polyfüllbaren Funktionen der Fall ist.

So können Sie testen, ob Ihre Laufzeit Object.assign

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

Wenn dies funktioniert, müssen Sie lediglich "es2017.object" in die Eigenschaft "compilerOptions"."lib" Ihrer tsconfig.json aufnehmen.

Wenn dies fehlschlägt, fügen Sie eine Polyfüllung wie diese hinzu, die in TypeScript geschrieben ist.

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

Und importiere es mit

import './polyfill-object-assign';

Nehmen Sie auch die gleichen Änderungen an Ihrer tsconfig.json vor wie für den zur Laufzeit unterstützten Fall.

Ich hoffe das hilft

@aluanhaddad vielen Dank für die Erkenntnisse. Mein Knoten unterstützt Object.assign basierend auf dem Experiment, zu dessen Ausführung Sie mich aufgefordert haben. Aber selbst nach dem Hinzufügen von "compilerOptions": { "lib": ["es2017.object"] } bekomme ich immer noch squiggles . Sollte ich es einfach ignorieren oder gibt es etwas, das ich tun kann, um es verschwinden zu lassen?

@aluanhaddad egal . Es funktioniert jetzt gut.

@aluanhaddad Ich hatte zuvor "compilerOptions": { "lib": ["es2017.object", "es6"] } als ich die squiggles . Das Entfernen von es6 schien sich aufzulösen, aber das erneute Ausführen meines Schluck-Skripts führt plötzlich zu einer neuen Reihe von Fehlern.
Meine 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"
    ]
}

Beispiel meiner neuen Fehler:

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

Gibt es also eine Möglichkeit, Ihre Empfehlung zu verwenden und dennoch Typoskript zum korrekten Kompilieren zu erhalten?

@ Johnbendi ja sicher.

Verwendung Ich habe lediglich vorgeschlagen, den spezifischen Eintrag "es2017.object" hinzuzufügen, da Ihre Anfrage spezifisch ist.
Ich glaube, dass "lib": ["es6"] nicht mehr korrekt ist und dass es "lib": ["es2015"] .
Versuchen Sie "lib": ["es2015", es2017.object"] oder nur "lib": ["es2017"] .

Ich denke, was fehlt, ist eine saubere Art zu sagen: "Ich habe eine es6-Polyfüllung und es ist nicht Ihr Geschäftsskript, nimm einfach an, dass ich es tue" :).

Denn das Setzen von target: "es6" macht das, kann aber vermutlich auch Code mit nicht polyfüllbaren es6-Funktionen generieren.

Das Erfordernis von Core-Js zwingt Sie explizit dazu, das Shim zu haben. Sie können keine Shimmy- und Non-Shimmy-Builds haben, da TS sich beschweren wird.

Das Hinzufügen von node_modules/typescript/lib/lib.es6.d.ts zu files in tsconfig.json macht das, aber ... sieht nicht so sauber aus ... (oder fehlt mir ein offensichtlicher Weg, dies zu erreichen?)

@himdel einfach benutzen

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

Es funktioniert perfekt - ich mache es seit Monaten.

Oder eine beliebige Teilmenge, die Sie wünschen:

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

Ich kann immer noch nicht herausfinden, wie das geht. Wenn ich target: "es5" tsc habe, wird Object.assign in einen Aufruf einer Polyfüllung umgewandelt. Das Hinzufügen von Bibliotheken ändert für mich überhaupt nichts.

In meinem Fall möchte ich, dass Pfeilfunktionen in normale Funktionen konvertiert werden, aber statische Aufrufe wie Object.assign und Array.includes unberührt lassen.

tsc sollte ein Flag haben, das anzeigt, dass nur Syntaxfunktionen transpiliert werden sollen und keine Polyfill-Funktionen wie statische Methoden Object , Array usw.

Wenn ich ein Ziel habe: "es5" tsc verwandelt Object.assign immer in einen Aufruf einer Polyfüllung.

@danez TypeScript ändert einen Anruf nicht in Object.assign . Klingt es so, als würden Sie Ihren Code über Babel ausführen?

@ RyanCavanaugh Es tut. Babel ist nicht in den Prozess involviert.
Was es tut, transformiert dies im Grunde:

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

das sehr gut finden

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 Kannst du einen tatsächlichen Repro posten? Der Compiler hat diesen Code nicht ausgegeben

Ich habe kein Repo, aber das ist die tsconfig:

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

und dann rufe ich einfach tsc

@danez Ich fürchte, du musst einen tatsächlichen Repro posten.

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, dass es richtig funktioniert . Der Compiler verfügt über einen Helfer für die Bereitstellung syntaktischer Unterstützung, schreibt jedoch die Funktionalität auf Globals nicht neu. @danez Auf diesen Helfer kann mit TypeScript-Code nie

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

wird emittieren:

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

Ich glaube nicht, dass Sie ein Beispiel liefern können, in dem Object.assign() in __assign umgeschrieben wird, sondern nur für die syntaktische Unterstützung von Objektverbreitungen verwendet wird.

@kitsonk Ja, ich habe Object Spread verwendet, Sie haben Recht. Es wäre also schön, wenn die Objektverbreitung einfach in Object.assign

@danez ist es, wenn Sie auf etwas zielen, das Object.assign() (z. B. Ziel ist es2015 +).

Zum Beispiel:

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

Wird ausgegeben:

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

@kitsonk Ja, ich weiß, aber ich ziele auf es5 _syntax_ ab, wobei alle ES2017 + -Globals von core-js polygefüllt werden. Ich beziehe mich also auf einen Modus, der die es5-Syntax ausgibt, aber davon ausgeht, dass alle integrierten Funktionen verfügbar sind. Ähnlich wie Babel mit der Option useBuiltins : https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx#usebuiltins

Im Allgemeinen geben wir uns nicht die Mühe, "gemischte" Laufzeitziele besonders zu unterstützen, aber es stehen andere Optionen zur Verfügung.

Sie können __assign in den globalen Bereich einfügen (zusammen mit anderen Helfern, z. B. __extends ) und mit --noEmitHelpers ausführen.

Ja, ich weiß, aber ich ziele auf es5 _syntax_

Ich denke, dies ist das Hauptproblem, wenn Funktionen wie Object.assign nicht angepasst werden. Ich muss auf die eine oder andere Weise Stellung beziehen - Sie können seine Verwendung nicht durch __assign ersetzen, wenn Sie einen Spread verwenden, aber nicht, wenn Sie ihn direkt aufrufen, es ist verdammt verwirrend

Sie können seine Verwendung nicht durch __assign ersetzen, wenn Sie einen Spread verwenden, aber nicht, wenn Sie ihn direkt aufrufen, es ist verdammt verwirrend

Ich kann verstehen, dass Sie das Gefühl haben, dass es verwirrend ist, aber wenn Sie das Mantra nicht einhalten, bietet TypeScript _syntatische Umschreibungen_ nicht _funktionale Polyfüllungen_. TypeScript ist in seinem Verhalten völlig konsistent und es wird eine Meinung dazu abgegeben, was es tut, und in 99% der Fälle hat es keine Auswirkungen auf den Endbenutzer.

Wie @RyanCavanaugh sagt, ist es möglich, eine Reihe von Polyfills plus tslib mit --noEmitHelpers plus einem globalen Skript zu nutzen, das besagt:

__assign = Object.assign;

Aber das ist wirklich eine "Bonusrunde" von TypeScript und es ist fraglich, ob es eine messbare Leistungsverbesserung in der realen Welt bieten würde.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen