Typescript: أي خارطة طريق لدعم تعيين كائن؟

تم إنشاؤها على ٩ يونيو ٢٠١٥  ·  47تعليقات  ·  مصدر: microsoft/TypeScript

في 1.6 أو 2.0؟

Question

التعليق الأكثر فائدة

أنا آسف لم أفهم. يدعم ES6 Object.assign لذلك يقوم Babel بنقله إلى polyfill لأهداف 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 pollyfill ، يمكنك دائمًا تضمين ملفك ، على سبيل المثال ، لدى Mozilla ملف pollyfill له.

إذا كنت تسأل عن كتابة نمط mixin ، فهذا شيء كان لدينا دائمًا على خريطة الطريق لـ 2.0.

شكر. أعني كتابة Object.assign(...) في TypeScript والذي ينتقل إلى es3 / es5 / es6. لذلك فهو موجود على خريطة الطريق لـ 2.0.

: +1:

unional ربما أساء فهم شيء ما. لكنني لا أعتقد أن استنتاجك صحيح.

يدعم ES6 على أي حال Object.assign ، لذلك إذا كان هدفك هو ES6 ، فيجب أن تكون قادرًا على كتابته في TypeScript على أي حال.

ولكن إذا قمت بالتحويل إلى es5 وما دونه ، فستظل بحاجة إلى polyfill ، لأن es5 لا يحتوي على Object.assign .

كحل سريع ، ضع في اعتبارك هذا polyfill والكتابة:

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

شكرا لبحث هذه القضية. كان منذ فترة طويلة. :) نعم ، أفهم الآن أنه يتطلب ببساطة polyfill لـ es5.

شكرا لك مرة أخرى!

هل سنقوم بتجميع هذا polyfill عندما يتم تعيين الهدف على es5؟ أعني ، نظرًا لأن هدف التجميع مضبوط على es5 ، فيجب أن يكون هذا قادرًا على العمل في بيئة es5.

هل سنقوم بتجميع هذا polyfill عندما يتم تعيين الهدف على es5

نعم ، كمهر حصان: روز:: حصان:

أنا آسف لم أفهم. يدعم ES6 Object.assign لذلك يقوم Babel بنقله إلى polyfill لأهداف ES5. أنا في حيرة من أمري لماذا لا يفعل TypeScript الشيء نفسه ، تمامًا كما يفعل مع ميزات ES6 الأخرى.

prashaantt TypeScript لا يوفر أي polyfills وهو حسب التصميم. إذا كنت ترغب في استخدام Object.assign أو أي طريقة / بدائية أخرى مضافة بواسطة معيار جديد ، فيجب عليك إما:

  • استخدم مكتبة polyfill (مثل core-js ) مع الأنواع المقابلة
  • استهدف المعيار عند إضافة الميزة (على سبيل المثال ، "target": "es6" في tsconfig.json )

@ devoto13 شكرًا ، core-js يعمل بشكل جيد.

كان لدي سؤال سخيف. بعد npm install ing و typings install ing core-js بدأت في الحصول على IntelliSense لطرق polyfilled. لكنني سأظل بحاجة إلى استيراد هذه الطرق فعليًا في كل وحدة نمطية أستخدمها فيها أو أن الكود المترجم لن يتضمن polyfill ، أليس كذلك؟

prashaantt بمجرد طلب core-js/shim لأي شيء ، سيكون Object.assign متاحًا عالميًا من تلك النقطة فصاعدًا. أوصي بوضع import 'core-js/shim'; في الجزء العلوي من الوحدة النمطية / نقطة الدخول الرئيسية.

شكراjesseschalken. كمتابعة ، ألا يؤدي استيراد shim بالكامل إلى تضخيم حزمي؟ أو هل سيكون tsc أو ts-loader ذكيًا بما يكفي لالتقاط الأشياء التي يتم استخدامها بالفعل في الكود الخاص بي؟

prashaantt يعتمد ذلك على المستعرضات التي تستهدفها. أنت تعلم بالفعل أن Object.assign لا يدعمه المتصفح الذي تستهدفه ، فمن يدري ما هو غير ذلك؟ أنت بحاجة إلى الرقاقة الكاملة في الحزمة الخاصة بك إذا كنت تريد دعم متصفح أوسع.

إذا كنت تريد فقط polyfill 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 للمولدات / انتظار غير متزامن.

شكرًا على النصائح ، على الرغم من أنه يجب أن يكون لدينا دعم كامل العقبة الأخيرة إلى 2.0!

أنا آسف لم أفهم. يدعم ES6 Object. assign بحيث يقوم Babel بنقله إلى polyfill لأهداف ES5. أنا في حيرة من أمري لماذا لا يفعل TypeScript الشيء نفسه ، تمامًا كما يفعل مع ميزات ES6 الأخرى.

prashaantt ماذا تقصد بقولك ان Babel _transpiles_ Object.assign ؟ إنها مجرد وظيفة. يمكنك إضافة polyfill ، ponyfill ، أو كتابته بنفسك ، ويمكنك استخدامه في أي بيئة - ES 3 ، 5.1 ، 6 ، إلخ.

aluanhaddad إن فهمي لما يفعله Babel هو إذا حددت es5 كهدفك واستخدمت Object.assign ، فإنه يتضمن تلقائيًا polyfill لـ 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'.

في مشروعك ربما تقوم بتضمين كتابة لبعض polyfill ، لكن لا تتضمن تنفيذ polyfill ، ولهذا السبب فشلت.

أفهم أن هذا هو السلوك الحالي هاها. وجهة نظري هي أنه إذا حددت الهدف على أنه es5 ، فسيكون من الجيد أن تعطيك الكتابة المطبوعة خطأً ذا مغزى يتجاوز "ليس في المُنشئ".

kyleholzinger FWIW ، يدعم TypeScript 2.1 الآن بقية / انتشار الكائن ES6 (ES7؟) لذلك أجد شخصيًا سببًا أقل للإزعاج بشأن Object.assign بعد الآن. ذلك مع المولدات الأصلية يعني أنني لست بحاجة إلى أي polyfills في معظم مشاريعي.

هذا صحيح. سيكون من اللطيف عدم استبعاد ميزات اللغة. سيكون أمرًا رائعًا إذا لم يكن Object.assign يعتمد على المتصفح وحذرك المطبوع عليه إذا لم تكن تستخدم polyfill.

إذا تم تعيين tsconfig الخاص بك بشكل صحيح ، فإن TypeScript يحذرك من عدم توفر assign لـ <ES6 من خلال عدم منحك خيار الإكمال التلقائي لاسم الوظيفة هذا على Object في المقام الأول. إذا أصررت على كتابتها يدويًا بشق الأنفس ، فسترى الخطوط الحمراء. إذا تجاهلت ذلك ، فسيعطيك tsc الخطأ أعلاه. لكن إذا تجاهلت ذلك أيضًا عن قصد ، فأنت تستحق عن حق هلاكك. ؛)

صحيح ، نقطتي الوحيدة في مواصفات es2015 ، لذا يجب أن تكون مكتوبة بخط مكتوب ؛)

كيف يمكنني استخدام Object.assign على العقدة أثناء استهداف es5 ؟ أي أن الكود يجب أن يعمل على الخادم وليس في المتصفح. هل أستخدم البوليفيل أيضًا وكيف؟

تضمين التغريدة
يعتمد ذلك على إصدار العقدة ، أي أنه يعتمد على وقت التشغيل كما هو الحال مع جميع الوظائف القابلة للتعبئة.

إليك كيفية إجراء اختبار إذا كان وقت التشغيل يدعم Object.assign

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

إذا نجح ما سبق ، فكل ما عليك فعله هو تضمين "es2017.object" في خاصية "compilerOptions"."lib" في tsconfig.json الخاص بك.

إذا فشل ما سبق ، أضف polyfill مثل هذا ، وهو مكتوب في 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 polyfill وهو ليس نصًا مكتوبًا لعملك ، فقط افترض أنني أفعل" :).

لأن تعيين 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 إلى بعض الاستدعاءات إلى polyfill. إضافة libs لا يغير أي شيء على الإطلاق بالنسبة لي.

في حالتي ، أريد تحويل وظائف الأسهم إلى وظائف عادية ، لكن اترك مكالمات ثابتة مثل Object.assign و Array.includes دون تغيير.

يجب أن يكون لدى tsc علامة لإخباره بميزات بناء الجملة فقط وليس ميزات polyfill مثل الطرق الثابتة Object ، Array إلخ.

عندما يكون لدي هدف: سيقوم "es5" tsc دائمًا بتحويل Object. assign إلى بعض الاستدعاءات إلى polyfill.

danez لن يقوم TypeScript بتعديل استدعاء لـ Object.assign . يبدو أنك ربما تقوم بتشغيل الكود الخاص بك من خلال 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 هل يمكنك نشر repro الفعلي؟ المترجم لم يرسل هذا الرمز

ليس لدي ريبو ، ولكن هذا هو 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({}, {});

unional من حيث أنه يعمل بشكل صحيح . يحتوي المترجم على مساعد لتوفير الدعم النحوي ، لكنه لا _ أبدًا _ يعيد كتابة الوظائف على globals. 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 + globals التي يتم تعبئتها بواسطة 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 ، من الممكن الاستفادة من مجموعة من polyfills ، بالإضافة إلى tslib ، مع --noEmitHelpers بالإضافة إلى برنامج نصي عالمي يقول:

__assign = Object.assign;

ولكن هذه هي ميزة TypeScript "جولة" حقًا ويمكن القول إنها ستوفر أي تحسين في الأداء يمكن قياسه في العالم الحقيقي.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات