Typescript: Есть какие-нибудь планы по поддержке Object.assign?

Созданный на 9 июн. 2015  ·  47Комментарии  ·  Источник: microsoft/TypeScript

в 1.6 или 2.0?

Question

Самый полезный комментарий

Извините, я не понимаю. ES6 поддерживает Object.assign поэтому Babel переносит его в полифилл для целей ES5. Я не понимаю, почему TypeScript не делает того же, что и другие функции ES6.

Все 47 Комментарий

не уверен, что вы ищете. если вы имеете в виду типизацию, она уже определена в lib.es6.d.ts как

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

Я имею в виду реализацию, TypeScript не включает в себя поллифиллы, вы всегда можете включить свою, например, в Mozilla есть поллифиллы для него.

Если вы спрашиваете о типизации паттернов миксинов, это то, что у нас всегда было в дорожной карте 2.0.

Благодарю. Я имею в виду написание Object.assign(...) на TypeScript, которое переносится на es3 / es5 / es6. Так что это в дорожной карте 2.0.

: +1:

@unional Я что-то неправильно понял. Но я не думаю, что ваш вывод верен.

ES6 в любом случае поддерживает Object.assign , поэтому, если ваша цель - ES6, вы все равно сможете написать его на TypeScript.

Но если вы перейдете на es5 и ниже, вам все равно понадобится полифилл, потому что в es5 нет Object.assign .

В качестве быстрого обходного пути рассмотрите этот полифил и ввод:

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

Спасибо, что раскопали эту проблему. Это было довольно давно. :) Да я теперь понимаю, что для es5 просто требуется полифилл.

Еще раз спасибо!

Скомпилируем ли мы этот полифилл, если для цели задано значение es5? Я имею в виду, поскольку цель компиляции установлена ​​на es5, тогда это должно работать в среде es5.

Скомпилируем ли мы этот полифилл, если target будет es5

Да, как пони: rose:: horse:

Извините, я не понимаю. ES6 поддерживает Object.assign поэтому Babel переносит его в полифилл для целей ES5. Я не понимаю, почему TypeScript не делает того же, что и другие функции ES6.

@prashaantt TypeScript не предоставляет полифилов, и это сделано специально. Если вы хотите использовать Object.assign или любой другой примитив / метод, добавленный более новым стандартом, вам следует либо:

  • использовать библиотеку polyfill (например, core-js ) с соответствующими типами
  • нацельтесь на стандарт при добавлении функции (например, "target": "es6" в вашем tsconfig.json )

@ DevOto13 Спасибо, core-js работает хорошо.

У меня просто был глупый вопрос. После npm install ing и typings install ing core-js я начинаю получать IntelliSense для полифиллированных методов. Но мне все равно нужно будет импортировать эти методы в каждый модуль, где я их использую, иначе скомпилированный код не будет включать полифиллы, верно?

@prashaantt Как только core-js/shim требуется для чего-либо, с этого момента Object.assign будет доступен глобально. Я рекомендую поставить import 'core-js/shim'; вверху вашего основного модуля / точки входа.

Спасибо @jesseschalken. Как продолжение, не приведет ли импорт всего shim к раздуванию моего пакета? Или tsc или ts-loader будет достаточно умным, чтобы подбирать только то, что действительно используется в моем коде?

@prashaantt Это зависит от браузеров, на которые вы ориентируетесь. Вы уже знаете, что Object.assign не поддерживается браузером, на который вы ориентируетесь, кто знает, что еще нет? Если вам нужна самая широкая поддержка браузером, вам понадобится вся прокладка в вашем комплекте.

Если вам нужен полифилл только для Object.assign , вы можете import 'core-js/modules/es6.object.assign'; и добавлять другие вещи, когда обнаружите, что они вам нужны (список см. В shim.js в core-js, также документы). Webpack будет следовать графику требований и включать только необходимые модули.

Если вы уже используете Babel, я рекомендую использовать import 'babel-polyfill'; вместо использования core-js напрямую. Он включает core-js/shim но также regenerator-runtime для генераторов / async-await.

Спасибо за советы, хотя у нас должна быть полная поддержка генераторов в любой момент - буквально последнее препятствие на пути к 2.0!

Извините, я не понимаю. ES6 поддерживает Object.assign, поэтому Babel переносит его в полифил для целей ES5. Я не понимаю, почему TypeScript не делает того же, что и другие функции ES6.

@prashaantt Что вы имеете в виду, когда говорите, что Babel _transpiles_ Object.assign ? Это просто функция. Вы можете добавить polyfill, ponyfill или написать его самостоятельно, и вы можете использовать его в любой среде - ES 3, 5.1, 6 и т. Д.

@aluanhaddad Я понимаю, что делает babel: если вы укажете es5 в качестве цели и используете Object.assign , он автоматически включает полифил для Object.assign , и не будет, если вы его не используете. Было бы неплохо, если бы машинописный текст делал то же самое, поскольку в нем утверждалось, что это «надмножество es2015», что на самом деле неверно, если он не обеспечивает функциональность для переноса на более старые цели. (Хотя я мог ошибаться)

@ DevOto13, если ваша цель - es5, вы должны как минимум выдать предупреждение о том, что Object.assign не поддерживается в es5. Нет смысла делать его полностью валидным и не сообщать программисту, что вам нужен какой-то случайный полифил.

@kyleholzinger то, что вы описали (таргетинг на ES5 означает, что Object.assign недоступен), уже является поведением.

@kyleholzinger Это действительно вызывает ошибку. Если вы создаете папку с двумя следующими файлами:

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

А затем запустите на нем tsc . Вы получите следующую ошибку:

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

В свой проект вы, вероятно, включаете типизацию для какого-то полифилла, но не включаете реализацию полифилла, поэтому это не удается.

Я понимаю, что это текущее поведение, ха-ха. Я хочу сказать, что если вы укажете цель как es5, было бы неплохо, если бы машинописный текст выдал вам значимую ошибку помимо «это не в конструкторе».

@kyleholzinger FWIW, TypeScript 2.1 теперь поддерживает отдых / распространение объекта ES6 (ES7?), поэтому я лично нахожу меньшую причину для беспокойства о Object.assign . Это с родными генераторами означает, что мне не нужны полифилы в большинстве моих проектов.

Это правда. Было бы неплохо не упускать языковые особенности. Было бы здорово, если бы Object.assign не зависел от браузера и машинописный текст предупреждал вас, если вы не использовали полифилл.

Если ваш tsconfig настроен правильно, TypeScript предупреждает вас о том, что assign недоступен для <ES6, не давая вам возможность автозаполнения этого имени функции в Object в первую очередь. Если вы будете продолжать усердно писать его вручную, вы увидите красные загогулины. Если вы проигнорируете это, tsc выдаст вам вышеуказанную ошибку. Но если вы намеренно игнорируете и это, вы по праву заслуживаете своей гибели. ;)

верно, моя единственная точка зрения находится в спецификации es2015, поэтому она должна быть в машинописном тексте;)

Как использовать Object.assign на узле при таргетинге на es5 ? То есть код должен работать на сервере, а не в браузере. Я тоже использую полифиллы и как?

@johnbendi
Это зависит от версии узла, то есть от среды выполнения, как и в случае со всеми функциональными возможностями полифилирования.

Вот как вы можете проверить, поддерживает ли ваша среда выполнения Object.assign

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

Если вышеуказанное работает, все, что вам нужно сделать, это включить "es2017.object" в свойство "compilerOptions"."lib" вашего tsconfig.json.

Если это не помогло, добавьте полифилл, подобный этому, который написан на 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;
  };
}

И импортируйте его с помощью

import './polyfill-object-assign';

А также внесите те же изменения в файл tsconfig.json, что и для случая, поддерживаемого средой выполнения.

Надеюсь, это поможет

@aluanhaddad большое спасибо за идеи. Мой узел поддерживает Object.assign на основе эксперимента, который вы меня просили. Но даже после добавления "compilerOptions": { "lib": ["es2017.object"] } я все равно получаю squiggles . Должен ли я просто проигнорировать это или я могу что-то сделать, чтобы это исчезло.

@aluanhaddad, неважно. Сейчас он работает хорошо.

@aluanhaddad У меня раньше было "compilerOptions": { "lib": ["es2017.object", "es6"] } когда я получал squiggles . Удаление es6 похоже, решило проблему, но повторный запуск сценария gulp снова неожиданно приводит к новому набору ошибок.
Мой 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"
    ]
}

Образец моих новых ошибок:

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

Итак, есть ли способ использовать вашу рекомендацию и при этом получить машинный текст для правильной компиляции?

@johnbendi да, конечно.

Использование Я просто предлагал добавить конкретную запись "es2017.object" из-за специфики вашего запроса.
Я считаю, что "lib": ["es6"] больше не является правильным и должно быть "lib": ["es2015"] .
Попробуйте "lib": ["es2015", es2017.object"] или просто "lib": ["es2017"] .

Я думаю, чего не хватает, так это чистого способа сказать: «У меня есть полифилл es6, и это не ваш машинописный текст, просто предположим, что я его использую» :).

Потому что установка target: "es6" делает это, но, предположительно, также может генерировать код с использованием неполифилируемых функций es6.

Требование core-js явно вынуждает вас использовать прокладку, у вас не может быть сборок с шимми и без шимми, потому что TS будет жаловаться.

Добавление node_modules/typescript/lib/lib.es6.d.ts к files в tsconfig.json делает это, но ... выглядит не так чисто ... (или мне не хватает очевидного способа добиться этого?)

@himdel просто используйте

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

Работает отлично - делаю уже несколько месяцев.

Или любое подмножество по вашему желанию:

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

Я до сих пор не могу понять, как это сделать. Когда у меня есть target: "es5" tsc всегда преобразует Object.assign в некоторый вызов полифилла. Добавление библиотек для меня вообще ничего не меняет.

В моем случае я хочу, чтобы стрелочные функции были преобразованы в обычные функции, но не трогали статические вызовы, такие как Object.assign и Array.includes .

tsc должен иметь флаг, указывающий только на функции синтаксиса транспиляции, а не на функции полифила, такие как статические методы Object , Array и т. д.

Когда у меня есть target: "es5" tsc всегда преобразует Object.assign в некий вызов полифилла.

@danez TypeScript не изменяет вызов Object.assign . Похоже, вы запускаете свой код через Babel?

@RyanCavanaugh Так и есть. Babel не участвует в процессе.
То, что он делает, в основном меняет это:

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

в это

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 Можете ли вы опубликовать репро? Компилятор не выдал этот код

У меня нет репо, но это tsconfig:

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

а затем я просто звоню tsc

@danez Боюсь, вам придется опубликовать репродукцию.

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

Может быть, связано: https://github.com/Microsoft/TypeScript/issues/12901

@unional в том, что он работает правильно . У компилятора есть помощник для обеспечения синтаксической поддержки, но он не _ никогда_ перезаписывает функциональность глобальных переменных. @danez, этот помощник никогда не доступен для кода TypeScript.

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

будет излучать:

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

Я не думаю, что вы можете привести пример, когда Object.assign() переписывается в __assign , он используется только в синтаксической поддержке распространения объектов.

@kitsonk Да, я использовал распространение объекта, вы правы. Так что было бы неплохо, если бы объект-распространение просто преобразовалось в Object.assign

@danez - это когда вы нацеливаетесь на то, что поддерживает Object.assign() (например, цель - es2015 +).

Например, это:

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

Выведет:

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

@kitsonk Да, я знаю, но я нацелен на es5 _syntax_, где все глобальные переменные ES2017 + полифицируются с помощью core-js. Итак, я имею в виду, что было бы неплохо, так это режим, который выводит синтаксис es5, но предполагает, что все встроенные функции доступны. Подобно тому, что делает babel с опцией useBuiltins : https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx#usebuiltins

В общем, мы не изо всех сил стараемся оказывать специальную поддержку "смешанным" целям времени выполнения, но есть и другие варианты.

Вы можете вставить __assign в глобальную область видимости (вместе с любыми другими помощниками, например, __extends ) и запустить с --noEmitHelpers .

Да, я знаю, но я нацелен на es5 _syntax_

Я чувствую, что это основная проблема, связанная с отсутствием мерцания таких функций, как Object.assign . Должен занять позицию так или иначе - вы не можете заменить его использование на __assign при использовании спреда, но не когда вы вызываете его напрямую, это чертовски запутанно

вы не можете заменить его использование на __assign при использовании распространения, но не тогда, когда вы вызываете его напрямую, это чертовски сбивает с толку

Я понимаю, что вы думаете, что это сбивает с толку, но это не так, если вы сохраните мантру, TypeScript предоставляет _синтатические перезаписи_, а не _функциональные полифилы_. TypeScript полностью соответствует тому, как он себя ведет, и он самоуверен в том, что он делает, и в 99% случаев он не оказывает никакого влияния на конечного пользователя.

Как говорит @RyanCavanaugh , можно использовать набор полифилов, плюс tslib , с --noEmitHelpers плюс глобальный скрипт, который говорит:

__assign = Object.assign;

Но это действительно «бонусный раунд» TypeScript, и можно утверждать, что он обеспечит любое измеримое улучшение производительности в реальном мире.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги