Typescript: اقتراح: السماح بالحصول على / تعيين أنواع مختلفة من الملحقات

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

سيكون أمرًا رائعًا إذا كانت هناك طريقة لتخفيف القيود الحالية التي تتطلب الحصول على / تعيين موصّلات من نفس النوع. سيكون هذا مفيدًا في موقف مثل هذا:

class MyClass {

    private _myDate: moment.Moment;

    get myDate(): moment.Moment {
        return this._myDate;
    }

    set myDate(value: Date | moment.Moment) {
        this._myDate = moment(value);
    }
}

حاليًا ، يبدو أن هذا غير ممكن ، ولا بد لي من اللجوء إلى شيء مثل هذا:

class MyClass {

    private _myDate: moment.Moment;

    get myDate(): moment.Moment {
        return this._myDate;
    }

    set myDate(value: moment.Moment) {
        assert.fail('Setter for myDate is not available. Please use: setMyDate() instead');
    }

    setMyDate(value: Date | moment.Moment) {
        this._myDate = moment(value);
    }
}

هذا أبعد ما يكون عن المثالية ، وسيكون الرمز أنظف كثيرًا إذا سُمح بأنواع مختلفة.

شكرا!

Design Limitation Suggestion Too Complex

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

يعد برنامج إعداد JavaScript مع أنواع مختلفة صالحًا تمامًا ويعمل بشكل رائع وأعتقد أنه الميزة / الغرض الرئيسي لهذه الميزة. الحاجة إلى تقديم setMyDate() فقط لإرضاء TypeScript يفسدها.

فكر أيضًا في مكتبات JS الخالصة التي ستتبع هذا النمط: سيتعين على d.ts الكشف عن اتحاد أو any .

تكمن المشكلة في أن الملحقات لا تظهر في ملفات .d.ts بشكل مختلف عن الخصائص العادية

ثم يجب إصلاح هذا القيد ويجب أن تظل هذه المشكلة مفتوحة:

// MyClass.d.ts

// Instead of generating:
declare class MyClass {
  myDate: moment.Moment;
}

// Should generate:
declare class MyClass {
  get myDate(): moment.Moment;
  set myDate(value: Date | moment.Moment);
}

// Or shorter syntax:
declare class MyClass {
  myDate: (get: moment.Moment, set: Date | moment.Moment);
  // and 'fooBar: string' being a shorthand for 'fooBar: (get: string, set: string)'
}

ال 125 كومينتر

أستطيع أن أرى كيف سيكون هذا لطيفًا هنا (وقد تم طلب ذلك من قبل على الرغم من أنني لا أستطيع العثور على المشكلة الآن) ولكن ليس من الممكن ما إذا كانت الأداة المساعدة كافية أم لا لتبرير ذلك. تكمن المشكلة في أن الموصّلات لا تظهر في ملفات .d.ts بشكل مختلف عن الخصائص العادية لأنها تظهر بنفس الشكل من هذا المنظور. بمعنى أنه لا يوجد تمايز بين المُحضر والمُعيِّن ، لذا لا توجد طريقة لأ) تتطلب تنفيذًا باستخدام موصِّل بدلاً من عضو مثيل واحد و ب) تحديد الاختلاف في الأنواع بين المُحضر و setter.

شكرا لك على الرد السريع ، دان. سأتبع الطريقة الأقل أناقة. شكرا على العمل العظيم!

يعد برنامج إعداد JavaScript مع أنواع مختلفة صالحًا تمامًا ويعمل بشكل رائع وأعتقد أنه الميزة / الغرض الرئيسي لهذه الميزة. الحاجة إلى تقديم setMyDate() فقط لإرضاء TypeScript يفسدها.

فكر أيضًا في مكتبات JS الخالصة التي ستتبع هذا النمط: سيتعين على d.ts الكشف عن اتحاد أو any .

تكمن المشكلة في أن الملحقات لا تظهر في ملفات .d.ts بشكل مختلف عن الخصائص العادية

ثم يجب إصلاح هذا القيد ويجب أن تظل هذه المشكلة مفتوحة:

// MyClass.d.ts

// Instead of generating:
declare class MyClass {
  myDate: moment.Moment;
}

// Should generate:
declare class MyClass {
  get myDate(): moment.Moment;
  set myDate(value: Date | moment.Moment);
}

// Or shorter syntax:
declare class MyClass {
  myDate: (get: moment.Moment, set: Date | moment.Moment);
  // and 'fooBar: string' being a shorthand for 'fooBar: (get: string, set: string)'
}

أدرك أن هذا مجرد رأي ، لكن كتابة أداة ضبط مثل a.x === y ليست true مباشرة بعد a.x = y; هي علامة حمراء عملاقة. كيف يعرف مستهلكو مكتبة كهذه الخصائص التي لها آثار جانبية سحرية وأيها لا؟

كيف [أنت] تعرف الخصائص التي لها آثار جانبية سحرية وأيها لا؟

في JavaScript يمكن أن يكون الأمر غير بديهي ، في TypeScript ستشتكي الأدوات (IDE + مترجم). لماذا نحب TypeScript مرة أخرى؟ :)

يعد برنامج إعداد JavaScript مع أنواع مختلفة صالحًا تمامًا ويعمل بشكل رائع وأعتقد أنه الميزة / الغرض الرئيسي لهذه الميزة.

هذا يجادل بأن JavaScript مكتوب بشكل ضعيف ، لذا يجب كتابة TypeScript بشكل ضعيف. :-س

هذا يجادل بأن JavaScript مكتوب بشكل ضعيف ، لذا يجب كتابة TypeScript بشكل ضعيف

C # يسمح بذلك وهذا لا يجعل هذه اللغة مكتوبة بشكل ضعيف C # لا تسمح بالحصول على / مجموعة من أنواع مختلفة.

C # يسمح بذلك وهذا لا يجعل هذه اللغة مكتوبة بشكل ضعيف C # لا تسمح بالحصول على / مجموعة من أنواع مختلفة.

:غمزة:

لا أحد يجادل (بقدر ما أستطيع أن أرى) أن الموصّلات يجب أن تكون مكتوبة بشكل ضعيف ، فهم يجادلون بأنه ينبغي أن تكون لدينا المرونة لتحديد النوع (الأنواع).

غالبًا ما تكون هناك حاجة لنسخ كائن قديم عادي إلى مثيلات الكائن.

    get fields(): Field[] {
      return this._fields;
    }

    set fields(value: any[]) {
      this._fields = value.map(Field.fromJson);
    }

هذا أجمل بكثير من البديل ويسمح لجهاز الإعداد الخاص بي بتغليف النوع الدقيق من أدوات تحديد المنطق التي تم صنعها من أجلها.

paulwalker ، النمط الذي تستخدمه هناك (واضع يأخذ any ويأخذ getter نوعًا أكثر تحديدًا) صالح اليوم.

@ دانكويرك عظيم أن تعرف ، شكرا لك! يبدو أنني احتجت فقط إلى تحديث برنامج التحويل البرمجي لـ IDE الخاص بي لـ ST.

danquirk لا يبدو أنه يعمل وفقًا للملعب (أو الإصدار 1.6.2):
http://www.typescriptlang.org/Playground#src =٪ 0A٪ 0Aclass٪ 20Foo٪ 20٪ 7B٪ 0A٪ 0A٪ 20٪ 20get٪ 20items ()٪ 3A٪ 20string٪ 5B٪ 5D٪ 20٪ 7B٪ 0A ٪ 09٪ 20٪ 20return٪ 20٪ 5B٪ 5D٪ 3B٪ 0A٪ 20٪ 20٪ 7D٪ 0A٪ 20٪ 20٪ 0A٪ 20٪ 20set٪ 20items (value٪ 3A٪ 20any)٪ 20٪ 7B٪ 0A٪ 09٪ 20٪ 20٪ 0A٪ 20٪ 20٪ 7D٪ 0A٪ 7D

لقد اختبرت للتو باستخدام typecript @ next (الإصدار 1.8.0-dev.20151102) ، ولدي أيضًا خطأ.

~$ tsc --version
message TS6029: Version 1.8.0-dev.20151102
~$ cat a.ts
class A {
    get something(): number {return 5;}
    set something(x: any) {}
}

~$ tsc -t es5 a.ts
a.ts(2,2): error TS2380: 'get' and 'set' accessor must have the same type.
a.ts(3,2): error TS2380: 'get' and 'set' accessor must have the same type.

ومن المفارقات ، بعد تحديث برنامج Sublime linter ، لم يعد يظهر أي خطأ ، والذي يستخدم TypeScript 1.7.x. كنت أفترض أنه تحسين قادم في 1.7+ ، لذلك ربما تراجع 1.8.0.

حتى مع إصدار كود الاستوديو المرئي (0.10.5 (ديسمبر 2015)) الذي يدعم الكتابة المطبوعة 1.7.5 ومع النسخة المطبوعة 1.7.5 المثبتة عالميًا على جهازي ، لا تزال هذه مشكلة:

image

إذن ما هو الإصدار الذي سيتم دعمه؟
شكرا

أعتقد أن دان كان مخطئًا. يجب أن يكون كل من getter و setter من نفس النوع.

عار. ستكون ميزة رائعة عند كتابة كائنات الصفحة لاستخدامها في اختبارات المنقلة.

كنت سأكون قادرًا على كتابة اختبار المنقلة:

po.email = "[email protected]";
expect(po.email).toBe("[email protected]");

... بتأليف كائن صفحة:

class PageObject {
    get email(): webdriver.promise.Promise<string> {
        return element(by.model("ctrl.user.email")).getAttribute("value")
    }
    set email(value: any) {
        element(by.model("ctrl.user.email")).clear().sendKeys(value);
    }
}

ماذا عن هذا الرمز يتطلب أن يكون المُعيِّن من النوع any ؟

ستُرجع أداة getter webdriver.promise.Promise<string> لكن المُحدِّد أريد تعيين قيمة string .

ربما يوضح الشكل الأطول التالي لاختبار المنقلة الأمر أكثر وضوحًا:

po.email = "[email protected]";
var currentEmail : webdriver.promise.Promise<string> = po.email;
expect(currentEmail).toBe("[email protected]")

RyanCavanaugh مع إدخال التعليقات التوضيحية الفارغة ، يمنع هذا الكود الذي يسمح باستدعاء أداة الضبط ذات القيمة null لتعيينها على قيمة افتراضية.

class Style {
    private _width: number = 5;

    // `: number | null` encumbers callers with unnecessary `!`
    get width(): number {
        return this._width;
    }

    // `: number` prevents callers from passing in null
    set width(newWidth: number | null) {
        if (newWidth === null) {
            this._width = 5;
        }
        else {
            this._width = newWidth;
        }
    }
}

هل يمكنك التفكير على الأقل في السماح للأنواع بالاختلاف في وجود | null و | undefined ؟

سيكون هذا حقا ميزة جميلة.

فهل ستكون هذه ميزة؟

artyil هو مغلق وموسم _By Design_ مما يشير إلى أنه لا يوجد حاليًا أي خطط لإضافته. إذا كانت لديك حالة استخدام مقنعة تعتقد أنها تتجاوز المخاوف التي تم التعبير عنها أعلاه ، فلا تتردد في طرح قضيتك وقد تجعل التعليقات الإضافية الفريق الأساسي يعيد النظر في موقفه.

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

أنا موافق.

بعد التفكير في هذا أكثر. أعتقد أن القضية هنا هي في الحقيقة تعقيد التنفيذ. أنا شخصياً أجد مثالArnavion مقنعًا ، لكن نظام الكتابة اليوم يتعامل مع الحاصل على / أدوات الضبط على أنها خصائص عادية. لكي يعمل هذا ، يجب أن يكون لكليهما نفس القيمة. لدعم أنواع القراءة / الكتابة سيكون تغييرًا كبيرًا ، ولست متأكدًا من أن الأداة المساعدة هنا تستحق تكلفة التنفيذ.

على الرغم من أنني أحب TypeScript وأقدر كل الجهود التي يبذلها الفريق فيها (حقًا ، يا رفاق!) ، يجب أن أعترف أنني محبط من هذا القرار. سيكون حلاً أفضل بكثير من البدائل getFoo()/setFoo() أو get foo()/set foo()/setFooEx() .

مجرد قائمة قصيرة من المشاكل:

  • نحن نفترض حاليًا أن الخصائص لها نوع واحد بالضبط. نحتاج الآن إلى التمييز بين النوع "قراءة" ونوع "الكتابة" لكل خاصية في كل موقع
  • تصبح جميع علاقات النوع أكثر تعقيدًا إلى حد كبير لأنه يتعين علينا التفكير في نوعين لكل خاصية بدلاً من نوع واحد (هل يمكن { get foo(): string | number; set foo(): boolean } لـ { foo: boolean | string | number } ، أو العكس؟)
  • نحن نفترض حاليًا أن الخاصية ، بعد تعيينها ، لا تزال تحتفظ بنوع القيمة المحددة في السطر التالي (والذي يبدو أنه افتراض خاطئ في قواعد أكواد بعض الأشخاص ، ماذا؟). ربما يتعين علينا فقط "إيقاف تشغيل" أي تحليل للتحكم في التدفق في خصائص مثل هذه

بصراحة ، أحاول حقًا الامتناع عن أن أكون توجيهيًا هنا حول كيفية كتابة التعليمات البرمجية ، لكنني أعترض حقًا على فكرة أن هذا الرمز

foo.bar = "hello";
console.log(foo.bar);

يجب أن تطبع أي شيء يتجاوز "hello" بلغة تحاول الحصول على دلالات عقلانية. يجب أن يكون للخصائص سلوك لا يمكن تمييزه عن الحقول للمراقبين الخارجيين.

RyanCavanaugh بينما أتفق معك في الرأي المحقون ، يمكنني أن أرى حجة مضادة واحدة مفادها أن _might_ فقط يكون من النوع TypeScripty جدًا ...

foo.bar = [ '1', 2 ];  // any[]
console.log(foo.bar);  // number[]: [ 1, 2 ]

على الرغم من أنني أميل شخصيًا إلى التفكير فيما إذا كنت ستصبح ساحرًا ، فمن الأفضل إنشاء طريقة بحيث يمكن للمطور النهائي أن يفهم بوضوح أن ما سيتم ثنيه ومطويًا وتشويهه.

هنا حالة استخدامنا لهذه الميزة. قدمت API الخاصة بنا إمكانية أطلقنا عليها اسم _Autocasting_. الميزة الرئيسية هي تجربة مطور مبسطة يمكنه التخلص من عدد الفئات المراد استيرادها لتعيين الخصائص التي تم تحديد النوع لها بشكل جيد.

على سبيل المثال ، يمكن التعبير عن خاصية اللون كمثيل Color أو كسلسلة CSS مثل rgba(r, g, b, a) أو كمصفوفة من 3 أو 4 أرقام. لا تزال تتم كتابة الخاصية كمثيل Color ، حيث إنه نوع ما تحصل عليه عند قراءة القيمة.

بعض المعلومات عنها: https://developers.arcgis.com/javascript/latest/guide/autocasting/index.html

لقد كان مستخدمونا سعداء جدًا بالحصول على هذه الميزة ، مما أدى إلى تقليل عدد عمليات الاستيراد المطلوبة ، ويفهمون تمامًا أن النوع يغير السطر بعد المهمة.

مثال آخر لهذه المشكلة: https://github.com/gulpjs/vinyl#filebase

file.base = 'd:\\dev';
console.log(file.base); //  'd:\\dev'
file.base = null;
console.log(file.base); //  'd:\\dev\\vinyl' (returns file.cwd)

إذن المحدد هو string | null | undefined و الحاصل هو string فقط. ما النوع الذي يجب أن نستخدمه في تعريفات الأنواع لهذه المكتبة؟ إذا استخدمنا السابق ، فسيتطلب المترجم عمليات تحقق فارغة عديمة الفائدة في كل مكان ، وإذا استخدمنا الأخير ، فلن نتمكن من تعيين null لهذه الخاصية.

لدي مثال آخر حيث أرغب في أن يعيد دالة getter القيمة nullable ، ولكن حيث يجب ألا تسمح أداة الإعداد بالقيمة null كمدخلات ، على النحو التالي:

class Memory {
    public location: string;
    public time: Date;
    public company: Person[];
}

class Person
{
    private _bestMemoryEver: Memory | null;

    public get bestMemoryEver(): Memory | null { // Might not have one yet
        return this._bestMemoryEver;
    }

    public set bestMemoryEver(memory: Memory) { // But when he/she gets one, it can only be replaced, not removed
        this._bestMemoryEver = memory;
    }
}

var someDude = new Person();
// ...
var bestMemory: Memory | null = someDude.bestMemoryEver;
//...
someDude.bestMemoryEver = null; // Oh no you don't!

أتفهم أنه قد يكون هناك الكثير من العمل لبناء بعض المنطق الخاص للسماح للحاصلين / المستوطنين بالاختلاف في القيمة الفارغة ، وهي ليست مشكلة كبيرة بالنسبة لي ، ولكن سيكون من الجيد الحصول عليها.

@ Elephant-Vessel من الناحية الفلسفية ، أحب المثال تمامًا ، فهو يمثل الطبيعة المتقلبة للبشر جيدًا ولكني لست مقتنعًا بأنه لن يمثل ذلك بشكل أفضل من خلال السماح بدولار null (أو undefined ) للإعداد. كيف يمكنني نمذجة فشل المشبك في النظام؟

aluanhaddad لماذا تريد فشل المشبك؟ لا أريد هذا النوع من الأشياء السيئة في كوني ؛)

أي تحديثات على هذا؟ ماذا عن وجود قيمة افتراضية عند ضبطها على فارغة أو غير محددة؟

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

فيما يلي ما أرغب في الحصول عليه:

export class TestClass {
  private _prop?: number;

  get prop(): number {
    // return default value if not defined
    this._prop === undefined ? 0 : this._prop;
  }
  set prop(val: number | undefined) {
    this._prop = val;
  }
}

يبدو لي أن الاستفادة من التحقق غير الفارغ تأتي مع مسكوكات ، وهذا واحد منهم. مع إيقاف تشغيل التحقق من القيم الفارغة ، يكون هذا ممكنًا ، لكنك لا تحصل على مساعدة المحول البرمجي لمنع الاستثناءات المرجعية الفارغة. ومع ذلك ، إذا كنت تريد مساعدة المترجم ، فأنا أشعر أنه يجب أن يأتي مع المزيد من الدعم مثل وجود تعريفات منفصلة للمكتسبين والمستوطنين فيما يتعلق على الأقل بإلغاء الإلغاء إذا لم يكن هناك شيء آخر.

تشير التسميات المتعلقة بالمشكلة إلى أنها قيود على التصميم وسيعتبر التنفيذ معقدًا للغاية ، مما يعني بشكل أساسي أنه إذا كان لدى شخص ما سبب مقنع للغاية لسبب وجوب حدوث ذلك ، فلن يحدث أي شيء.

mhegazykitsonk قد أكون متحيزًا ، لكنني أشعر أن هذا خطأ ظهر للتحقق من القيمة الفارغة الصارمة على نمط شائع ، خاصة في الدعائم المجعدة الأخرى مثل اللغات التي لا يوجد فيها فحص فارغ بعد. قد يتطلب الحل البديل من المستهلك استخدام عامل التشغيل بانج أو التحقق من أنه ليس فارغًا أبدًا (وهي نقطة عدم كونه فارغًا باستخدام القيم الافتراضية).

ينهار هذا بمجرد إضافة فحص صارم من القيم الفارغة لأنه الآن أنواع مختلفة تقنيًا. أنا لا أطلب تعيين أنواع مختلفة قوية ، ولكن يبدو أن متطلبات التصميم لتمكين هذا من شأنه أيضًا تمكين أنواع مختلفة قوية أيضًا.

يمكن استخدام تصميم بديل للأنواع الضعيفة ، مثل تلك الأنواع مثل null و undefined ستكون ذات غلاف خاص لتعريفات الواجهة وملفات d.ts إذا كانوا لا يريدون تمكين أنواع مختلفة تمامًا.

ردًا على https://github.com/Microsoft/TypeScript/issues/2521#issuecomment -199650959
إليك تصميم مقترح يجب أن يكون أقل تعقيدًا في التنفيذ:

export interface Test {
  undefset prop1: number; // property [get] type number and [set] type number | undefined
  nullset prop2: number; // property [get] type number and [set] type number | null
  nilset prop3: number; // property [get] type number and [set] type number | null | undefined
  undefget prop4: number; // property [get] type number | undefined and [set] type number
  nullget prop5: number; // property [get] type number | null and [set] type number
  nilget prop6: number; // property [get] type number | null | undefined and [set] type number
}

يبدو أن هناك بعض الأشخاص الذين يشاهدون هذا الموضوع وهم أكثر دراية بـ TypeScript ، لذلك ربما يمكن لشخص ما زال منتبهًا أن يجيب على سؤال ذي صلة. في قضية السيزيوم هذه ، ذكرت قيود نوع get / set التي نناقشها هنا ، وقال أفراد السيزيوم إن النمط الذي يتبعونه يأتي من C # - إنه المُنشئ الضمني .

هل يمكن أن يدعم TypeScript المنشئات الضمنية؟ بمعنى ، هل يمكنني القول أن myThing.foo يُرجع دائمًا Bar ، ويمكن تعيين Bar مباشرة ، ولكن يمكن أيضًا تعيين number والذي سوف يتم تغليفها بهدوء / استخدامها لتهيئة Bar ، كوسيلة راحة للمطور؟ إذا كان من الممكن القيام بذلك عن طريق التعليق التوضيحي Bar ، أو ربما بالقول تحديدًا أن " number قابل للتخصيص لـ Bar<number> " ، فسيتناول حالة الاستخدام التي تمت مناقشتها في قضية السيزيوم ، وكذلك العديد من القضايا التي أثيرت في هذا الموضوع.

إذا لم يكن الأمر كذلك ، فهل أحتاج إلى اقتراح دعم مُنشئ ضمني في إصدار منفصل؟

كلما فكرت / قرأت عن ذلك ، كلما كنت متأكدًا أكثر من أن "نمط المُنشئ الضمني" سيحتاج إلى الميزة الموضحة في هذه المشكلة. الطريقة الوحيدة الممكنة في Vanilla JS هي استخدام موصّلات الحصول على / تعيين الكائن ، لأن هذه هي المرة الوحيدة التي يستدعي فيها التعيين الاسمي ( = عامل) وظيفة محددة من قبل المستخدم. (أليس كذلك؟) لذا ، أعتقد أننا سنحتاج حقًا

class MyThing{
  set foo(b: Bar<boolean> | boolean);
  get foo(): Bar<boolean>;
}

الذي يبدو وكأن RyanCavanaugh يعتقد أنه ليس "دلالات عاقل".

الحقيقة البسيطة هي أن هناك مكتبة JS مشهورة جدًا تستخدم هذا النمط ، ويبدو أنه من الصعب إن لم يكن من المستحيل وصف قيود TS الحالية. آمل أن أكون مخطئا.

يسمح JavaScript بالفعل بالنمط الذي تصفه. التحدي _ هو أن جانب القراءة والكتابة للأنواع في TypeScript يفترض أنهما متماثلان حسب التصميم. كان هناك تعديل على اللغة لعدم السماح بالمهمة ( readonly ) ولكن هناك بعض المشكلات التي طلبت مفهوم _write only_ ، والتي تمت مناقشتها على أنها معقدة للغاية ولا قيمة لها في العالم الحقيقي.

IMO ، منذ أن سمحت JavaScript بوصول ، من المحتمل أن يكون الأشخاص قد أنشأوا واجهات برمجة تطبيقات مربكة معهم. أنا شخصياً أجد أنه من المحير أن شيئًا ما في المهمة _ سحريًا_ يتغير إلى شيء آخر. أي شيء ضمني ، وخاصة تحويل الكتابة ، هو لعنة JavaScript IMO. المرونة هي التي تسبب المشاكل. مع هذا النوع من التحويلات ، حيث يوجد _magic_ ، أنا شخصياً أحب أن أرى طرقًا يتم استدعاؤها ، حيث يكون من الواضح جدًا للمستهلك أن نوعًا من ✨ سيحدث ويجعل القراءة فقط لاسترداد القيم.

هذا لا يعني أن استخدام العالم الحقيقي غير موجود ، فمن المحتمل أن يكون ذلك منطقيًا ومنطقيًا. أعتقد أن الأمر يتعلق بتعقيد تقسيم نظام الكتابة بأكمله إلى قسمين ، حيث يجب تتبع الأنواع في ظروف القراءة والكتابة الخاصة بهم. يبدو أن هذا سيناريو غير تافه للغاية.

أوافق على: التألق: هنا ، لكنني لا أجادل مع أو ضد استخدام النمط ، أنا فقط أحاول أن أكون خلف الكود الذي يستخدمه بالفعل ووصف شكله. إنها تعمل بالفعل بالطريقة التي تعمل بها ، ولا تعطيني TS الأدوات اللازمة لوصفها.

للأفضل أو للأسوأ ، أعطتنا JS القدرة على تحويل المهام إلى استدعاءات وظيفية ويستخدمها الأشخاص. بدون القدرة على تعيين أنواع مختلفة لأزواج set/get ، فلماذا يكون لديك set/get في الكتابة المحيطة على الإطلاق؟ لا يتعين على Salsa أن تعرف أن خاصية ما يتم تنفيذها باستخدام برنامج getter and setter إذا كانت ستتعامل دائمًا مع myThing.foo كمتغير عضو من نوع واحد بغض النظر عن جانب المهمة التي تقوم بها. (من الواضح أن تجميع TypeScript الفعلي هو شيء آخر تمامًا).

تضمين التغريدة

يبدو أن هناك بعض الأشخاص الذين يشاهدون هذا الموضوع وهم أكثر دراية بـ TypeScript ، لذلك ربما يمكن لشخص ما زال منتبهًا أن يجيب على سؤال ذي صلة. في قضية السيزيوم هذه ، ذكرت قيود نوع get / set التي نناقشها هنا ، وقال أفراد السيزيوم إن النمط الذي يتبعونه يأتي من C # - إنه المُنشئ الضمني.

تؤدي عوامل التحويل المحددة من قبل المستخدم الضمنية لـ C # إنشاء رمز ثابت بناءً على أنواع القيم. يتم مسح أنواع TypeScript ولا تؤثر على سلوك وقت التشغيل ( async / await edge لحالات Promise polyfillers لا تتحمل).

kitsonk يجب أن أختلف بشكل عام فيما يتعلق بالممتلكات. بعد أن أمضيت وقتًا طويلاً مع Java و C ++ و C # ، أحب تمامًا الخصائص (كما هو موضح في C #) لأنها توفر تجريدًا نحويًا هامًا (هذا صحيح إلى حد ما في JavaScript). إنها تسمح للواجهات بفصل إمكانيات القراءة / الكتابة بطرق ذات مغزى دون استخدام تغيير بناء الجملة. أكره رؤية أفعال ضائعة في عمليات تافهة مثل getX() عند الحصول على X يمكن أن يكون ضمنيًا.
واجهات برمجة التطبيقات (APIs) المربكة لـ IMO ، وكثير منها تفعل ما تقوله يسيء استخدام الموصّلات ، تنبع أكثر من عدد كبير جدًا من الأحرف التي تقوم بأشياء سحرية.

إذا كان لدي عرض للقراءة فقط ، ولكن مباشر لبعض البيانات ، على سبيل المثال registry ، أعتقد أن خصائص القراءة فقط سهلة الفهم.

interface Entry {key: string; value: any;}

export function createRegistry() {
  let entries: Entry[] = [];
  return {
    register(key: string, value: any) {
      entries = [...entries, {key, value}];
    },
    get entries() {
      return [...entries];
    }
  }
}

const registry = createRegistry();

registry.register('hello', '您好');
console.log(registry.entries); //[{key: 'hello', value: '您好'}]
registry.register('goodbye', '再见');
console.log(registry.entries); //[{key: 'hello', value: '您好'}, {key: 'goodbye', value: '再见'}]

آسف على الظل ، لكني أحب الملحقات لهذا وأعتقد أنه من السهل فهمها ولكنني على استعداد للاقتناع بخلاف ذلك وقابلية القراءة هي شاغلي الأول.

عندما يحد TypeScript من JavaScript ، فإنه يصبح مصدر إزعاج أكثر من كونه ميزة. أليس TypeScript يهدف إلى مساعدة المطورين على التواصل مع بعضهم البعض؟

أيضًا ، يُطلق على الواضعين اسم Mutators لسبب ما. إذا لم أكن بحاجة إلى أي نوع من التحويل ، فلن أستخدم أداة ضبط ، سأقوم بتعيين المتغير بنفسي.

نقل مشروع جافا سكريبت للطباعة. واجهت هذه المشكلة ..

سيكون هذا لطيفًا لاستخدامه في الزاوي Input decorators. نظرًا لأن القيمة يتم تمريرها من قالب ، فإنه سيجعل التعامل مع أنواع الكائنات الواردة المختلفة أكثر نظافة ، في رأيي.

تحديث: يبدو أن هذا يعمل بالنسبة لي

import { Component, Input } from '@angular/core';
import { flatMap, isString, isArray, isFalsy } from 'lodash';

@Component({
  selector: 'app-error-notification',
  templateUrl: './error-notification.component.html',
})

export class ErrorNotificationComponent {
  private _errors: Array<string> = [];
  constructor() { }
  /**
   * 'errors' is expected to be an input of either a string or an array of strings
   */
  @Input() set errors(errors: Array<string> | any){
      // Caller just passed in a string instead of an array of strings
      if (isString(errors)) {
        this._errors = [errors];
      }
      // Caller passed in array, assuming it is a string array
      if (isArray(errors)) {
        this._errors = errors;
      }
      // Caller passed in something falsy, which means we should clear error list
      if (isFalsy(errors)) {
        this._errors = [];
      }
      // At this point just set it to whatever might have been passed in and let
      // the user debug when it is broken.
      this._errors = errors;
  }

  get errors() {
    return this._errors;
  }
}

فهل نحن كذلك؟ أرغب حقًا في الحصول على إمكانية إرجاع نوع مختلف باستخدام أداة التجميع غير التي يستخدمها جهاز الإعداد. علي سبيل المثال:

class Field {
  private _value: string;

  get value(): string {
    return this._value;
  }

  set value(value: any) {
    this._value = String(value);
  }
}

هذا ما يفعله 99٪ من عمليات التنفيذ الأصلية (إذا قمت بتمرير رقم إلى (input as HTMLInputElement).value ، فستعيد دائمًا سلسلة. في الواقع ، يجب اعتبار get and set طرقًا ويجب أن تسمح ببعض:

set value(value: string);
set value(value: number);
set value(value: any) {
  this._value = String(value);
}
  // AND/OR
set value(value: string | number) {
  this._value = String(value);
}

raysuelzer ، عندما تقول أن شفرتك "تعمل" ، ألا يُرجع ErrorNotificationComponent.errors نوعًا من Array<string> | any ؟ هذا يعني أنك بحاجة إلى حراس كتابة في كل مرة تستخدمها ، عندما تعلم أنه لا يمكنه إرجاع Array<string> .

@ lifaon74 ، على حد علمي لا يوجد تحرك في هذا الموضوع. أعتقد أنه قد تم تقديم حالة مقنعة - تم تقديم سيناريوهات متعددة ، وأطنان من كود JS القديم الذي لا يمكن وصفه بشكل صحيح في Typescript بسبب هذا - ولكن تم إغلاق المشكلة. ربما إذا كنت تعتقد أن الحقائق على الأرض قد تغيرت ، فافتح واحدة جديدة؟ لا أعرف سياسة الفريق بشأن إعادة صياغة الحجج القديمة ، لكني سأدعمك.

لا أعرف سياسة الفريق بشأن إعادة صياغة الحجج القديمة ، لكني سأدعمك.

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

شعوري الشخصي هو أنه سيكون من اللطيف أن يكون لديك ، خاصة بالنسبة للقدرة على تصميم كود JavaScript موجود يستخدم هذا النمط بشكل فعال.

أنا شخصياً سأعتبر أن الأمر قد تم حله إذا توصلنا إلى بعض الحلول غير المرهقة تمامًا لمشكلة JS القديمة. ما زلت نوعًا ما من نوع TS greenhorn ، لذلك ربما لا يكون الحل الحالي (الصب القسري أو حراس النوع غير الضروري) هو الاستخدام الأمثل للقدرات الحالية؟

يوافق على. سيكون setter محدودًا للغاية عندما يجب أن يكون النوع هو نفسه ... يرجى التحسين.

البحث عن توصيات لإنجاز هذا:

get price() {
    return (this._price as number);
  }

  set price(price) {
    this._price = typeof price === 'string' ? parseFloat(parseFloat(price).toFixed(8)) : parseFloat(price.toFixed(8));
  }

أريد أن تكون القيمة المخزنة رقمًا ولكني أريد التحويل من سلسلة إلى تعويم في أداة الضبط ، لذلك لا يتعين علي تحليل تعويم في كل مرة أقوم بتعيين خاصية.

أعتقد أن الحكم هو أن هذا نمط جافا سكريبت صالح (أو مقبول بشكل معقول على الأقل) ، ولا يتناسب بشكل جيد مع الأجزاء الداخلية للنسخ. ربما إذا كان تغيير العناصر الداخلية يمثل تغييرًا كبيرًا جدًا ، فيمكننا الحصول على نوع من التعليقات التوضيحية التي تغير كيفية تجميع TS داخليًا؟ شيء مثل

class Widget {
  get price(): number | /** <strong i="7">@impossible</strong> */ string | undefined { return this._price; }
  set price(val: number|string|undefined){ ... }
}

let w = new Widget();
w.price = 10;
// Annotation processes as "let p:number|undefined = (w.price as number|undefined)"
let p: number|undefined = w.price;

بمعنى آخر ، ربما يمكننا ترميز TS بحيث يقوم المعالج المسبق (؟) بتحويل جميع القراءات من w.price إلى فريق عمل صريح ، قبل تحويل TS إلى JS. بالطبع ، لا أعرف التفاصيل الداخلية لكيفية إدارة TS للتعليقات التوضيحية ، لذلك قد يكون هذا مجرد هراء كلي ، لكن الفكرة العامة هي أنه ربما يكون من الأسهل بطريقة ما تحويل TS إلى TS أخرى ، بدلاً من تغيير طريقة محول TS. يولد JS.

لا أقول أن هذه الميزة ليست ضرورية ولكن إليك طريقة لتجاوز set . يجب أن تُرجع IMO get دائمًا نفس نوع المتغير ، لذلك كان هذا جيدًا بما يكفي بالنسبة لي.

class Widget {
    get price(): number { return this._price; }
    set price(val){ return this.setPrice(val); } // call another function

    // do processing here
    private setPrice(price: number | string): number {
        let num = Number(price);
        return isNaN(num) ? 0 : num;
    }
}

iamjoyce widget.price = '123' يرسل خطأ التجميع في التعليمات البرمجية الخاصة بك

iamjoyce => خطأ لأن المترجم يفترض val: number في set price(val)

نعم ، أنا هنا أيضًا لأن استخدام Angular للمكونات يبدو أنه يريدنا أن يكون لدينا أنواع مختلفة من أجهزة getter / setter.

ستقوم Angular دائمًا بحقن سلسلة إذا قمت بتعيين خاصية مكونات (عبر سمة) من الترميز (ما لم تستخدم تعبيرًا ملزمًا) بغض النظر عن نوع خاصية الإدخال. لذلك من المؤكد أنه سيكون من الجيد أن تكون قادرًا على عمل نموذج على هذا النحو:

private _someProperty: SomeEnum;
set someProperty(value: SomeEnum | string) {
   this._someProperty = this.coerceSomeEnum(value);
} 
get someProperty(): SomeEnum {
  return this._someProperty;
}

اليوم ، يعمل هذا إذا تركنا | string لكن الحصول على ذلك سيصف بدقة أكبر كيف ينتهي Angular باستخدام المكون. في ذلك ، إذا قمت بالوصول إليه من التعليمات البرمجية ، فإن نوع الخاصية مهم ، ولكن إذا قمت بتعيينها مثل سمة ، فإنها ستفرض سلسلة في.

لا أعتقد أننا نريد هذه الوظيفة بشكل عام لأننا نرغب في تصميم واجهات برمجة التطبيقات بهذه الطريقة. أوافق على أن الخصائص أفضل إذا لم تسبب آثارًا جانبية قسرية تحت الغطاء. للتغلب على هذا ، سيكون من الرائع لو تم تصميم Angular بحيث تأتي مجموعات السمات مقابل مجموعات خصائص الربط في نقاط دخول مختلفة.

ولكن إذا نظرنا إلى هذا بشكل عملي ، فمن المفيد أن تسمح لنا TypeScript بنمذجة التفاعلات مع المكتبات الخارجية التي تصادف أنها تستخدم أنواعنا بطريقة تتحدى بديهية المساواة بين النوعين للقراءة / الكتابة.

في هذه الحالة ، سيكون من الصعب للغاية استخدام نوع الاتحاد على getter ، حيث سيتطلب كل أنواع الحراس / التأكيدات المزعجة. لكن ترك نوع الاتحاد بعيدًا عن المُعيِّن يبدو أمرًا خاطئًا لأن أي أداة من الأدوات يمكن أن تقرر لاحقًا بدء محاولة التحقق من أن الخصائص التي يتم تعيينها من السمات يجب أن تكون قابلة للتخصيص من السلسلة.

لذلك في هذه الحالة ، لا يكون نظام الكتابة معبرًا بدرجة كافية لالتقاط كيفية السماح لمكتبة خارجية باستخدام النوع. هذا لا يهم _ في الحال_ ، لأن المكتبة الخارجية لا تستهلك هذه الأنواع في الواقع على مستوى الكتابة المطبوعة ، مع معلومات النوع. ولكن قد يكون مهمًا في النهاية ، حيث قد تستهلك الأدوات معلومات النوع جيدًا.

ذكر أحد الأشخاص أعلاه حلاً بديلاً يبدو بغيضًا بعض الشيء ، ولكن من المحتمل أن ينجح ، حيث يمكننا استخدام نوع الاتحاد لكل من المُحضر / الحاصل ، ولكن لدينا طريقة ما للإشارة إلى أن أنواعًا معينة من الاتحاد مستحيلة بالنسبة للمُحضر. لذلك ، في الواقع ، تمت إزالتها مسبقًا من الاعتبار في تدفق التحكم في موقع استدعاء getter كما لو أن شخصًا ما قد استخدم حارسًا من النوع للتحقق من عدم وجوده. يبدو هذا مزعجًا مقارنة بالسماح بتكوين مجموعة فائقة على جهاز الإعداد مقارنةً باتحاد مجموعة فرعية على جهاز التجميع ، على الرغم من ذلك.

ولكن ربما تكون هذه طريقة لحل الأمور داخليًا. طالما أن الواضع هو مجرد اتحاد شامل لنوع getter. تعامل مع أنواع getter و setter على أنها مكافئة داخليًا ، لكن ضع علامة على أجزاء من نوع اتحاد getter على أنها مستحيلة. بحيث يزيلها تحليل تدفق التحكم من الاعتبار. هل سيتغلب ذلك على قيود التصميم؟

لتوضيح ما ورد أعلاه. ربما يكون من المفيد ، من وجهة نظر التعبير عن النوع ، أن تكون قادرًا على الإشارة إلى أجزاء من النوع المركب على أنها مستحيلة. لن يؤثر هذا على المساواة مع نوع آخر بنفس البنية ولكن بدون المعدلات المستحيلة. المعدل المستحيل سيؤثر فقط على تحليل تدفق التحكم.

كاقتراح بديل ، إذا كان هناك بعض بناء الجملة لتطبيق حارس من النوع المحدد من قبل المستخدم على قيمة إرجاع ، فسيكون هذا كافياً أيضًا. أدرك أن هذا ، في ظل الظروف العادية ، سخيف بعض الشيء ، لكن امتلاك هذا النوع من التعبير عن القيمة المرتجعة وكذلك في الحجج سيساعد في حل هذه الحالات الحادة.

نوع الحارس الذي يحدده المستخدم على القيمة المرتجعة ، له أيضًا ميزة أنه شيء يمكن التعبير عنه في إعلانات / واجهات النوع عند الضغط على خاصية؟

بمجرد إضافة حالة استخدام أخرى هنا ، تسمح mobx-state-tree بتعيين خاصية بطريقتين مختلفتين (من أنواع المثيلات واللقطات) ، ولكنها ستعيدها فقط بطريقة قياسية واحدة (مثيلات) من get accessor ، لذلك سيكون هذا مفيدًا للغاية إذا تم دعمه.

أنا أتجول في هذا مثل:

interface SomeNestedString {
  foo: string;
}

...

private _foo: SomeNestedString | string;

get foo(): SomeNestedString | string {
  return this._foo;
}

set foo(value: SomeNestedString | string) {
  this._foo = (value as SomeNestedString).foo;
}

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

أنا أستخدم any للتغلب على TSLint. والمترجم لا يشتكي أيضًا.

export class FooBar {
  private bar: string;

  get foo (): string | any {
    return this.bar;
  }

  set foo (value: Date | string | any) {
    // Type guarding enables IntelliSense in VS Code
    if (value instanceof Date) {
      this.bar = value.toISOString();
    } else if (typeof value === 'string') {
      this.bar = value;
    } else {
      this.bar = String(value); // Or throw an error
    }
  }
}

jpidelatorre بهذه الطريقة تفقد نوع الأمان.

const fooBar = new FooBar()
const a: number = fooBar.foo // works while it should fail
fooBar.foo = 123 // fails only at runtime, not compile time. It doesnt fail in this particular case with strings and numbers, but it will with something more complex

keenondrums هذا هو بالضبط سبب رغبتنا في امتلاك أنواع مختلفة من الموصلات. ما وجدته هو حل بديل وليس حلاً.

jpidelatorre الحل الحالي لدي هو استخدام وظيفة أخرى كواضع

export class FooBar {
  private bar: string;

  get foo (): string {
    return this.bar;
  }

  setFoo (value: Date | string ) {}
}

@ keenondrums ليس حسن المظهر مثل الموصلات ، ولكنه الخيار الأفضل حتى الآن.

ليس نموذجًا لما يحدث مع خصائص الإدخال في مكون Angular ، إلا إذا كنت تستخدم دالة منفصلة لـ getter.

وهو مجرد قبيح جدا.

أرغب أيضًا في هذه الميزة ، لكنني بحاجة إليها للعمل بشكل جيد في ملفات .d.ts ، حيث لا توجد حلول بديلة. أحاول توثيق بعض الفئات المكشوفة عبر Mocha (جسر Objective-C / Javascript) ، ويتم إعداد خصائص المثيل للعناصر المغلفة على النحو التالي:

class Foo {
    get bar:()=>number;
    set bar:number;
}

const foo = new Foo();
foo.bar = 3;
foo.bar(); // 3

لقد رأيت أيضًا الكثير من الحالات التي تسمح لك فيها واجهة برمجة التطبيقات بتعيين خاصية مع كائن يطابق الواجهة ، ولكن يقوم getter دائمًا بإرجاع مثيل فئة حقيقي:

interface IFoo {
    bar: string;
}

class Foo implements IFoo {
    bar: string;
    toString():string;
}

class Example {
    get foo:Foo;
    set foo:Foo|IFoo;
}

لقد نسيت هذه المسألة إلى حد كبير ، لكن فكرت في ذهني أثناء قراءتها مرة أخرى. أعتقد أننا استقرنا على فكرة أن هذا يستحق القيام به بشكل مجرد ، ولكنه معقد جدًا من الناحية الفنية بحيث لا يمكن تحقيقه. هذه عملية حسابية للمفاضلة - إنها ليست مستحيلة من الناحية الفنية ، فهي لا تستحق الوقت والجهد (وتعقيد الكود الإضافي) لتنفيذه. حق؟

هل حقيقة أن هذا يجعل من المستحيل وصف واجهة برمجة تطبيقات DOM الأساسية يؤدي إلى تغيير الرياضيات بدقة على الإطلاق؟ RyanCavanaugh يقول

أنا أعترض حقًا على فكرة أن هذا الرمز foo.bar = "hello"; console.log(foo.bar); يجب أن يطبع أي شيء بخلاف كلمة "hello" بلغة تحاول الحصول على دلالات عاقلة.

يمكننا أن نتجادل حول ما إذا كان يجب استخدامه بهذه الطريقة ، لكن DOM دعم دائمًا تركيبات مثل el.hidden=1; console.log(el.hidden) // <-- true, not 1 . هذا ليس مجرد نمط يستخدمه عدد قليل من الناس ، ولا يقتصر الأمر على وجوده في مكتبة شائعة ، لذا قد يكون دعمه فكرة جيدة. إنه مبدأ أساسي لكيفية عمل JS دائمًا - جزء من DWIM مخبأ في روح اللغة - وجعله مستحيلًا على مستوى اللغة يكسر المبدأ التأسيسي لـ TS ، وهو أنه يجب أن يكون "مجموعة شاملة" من JS . إنها فقاعة قبيحة تخرج من مخطط فين ، ولا ينبغي أن ننساها.

هذا هو السبب في أنني ما زلت أحب فكرة الحفاظ على نفس النوع:

number | boolean

ولكن عند تقديم نوع من أنواع الطباعة حيث يمكنك الإشارة إلى أنه ، في حين أن لدى getter تقنيًا نفس نوع الاتحاد ، فإنه في الواقع لن يقوم إلا بإرجاع مجموعة فرعية من الأنواع الموجودة في الاتحاد. ألا يجعل التعامل معها على أنها نوع من تضييق الأدوات على القاطع (شيء تدفق الاستدلال؟) أكثر وضوحًا من تعديل نموذج الكتابة؟ (يقول إنه لا يعرف شيئًا عن الباطن ...)

يمكن أن يكون هذا ، بدلاً من ذلك ، ضمنيًا فقط إذا كان النوع المستخدم في أداة التجميع عبارة عن مجموعة فرعية صارمة من نوع المحددات؟

يمكن أن يكون هذا ، بدلاً من ذلك ، ضمنيًا فقط إذا كان النوع المستخدم في أداة التجميع عبارة عن مجموعة فرعية صارمة من نوع المحددات؟

👍!

أعتقد أنه يمكننا أن نتفق جميعًا على أنه لا ينبغي أن تكون قادرًا على الحصول على نوع غير قابل للتحديد ؛ أرغب فقط في طريقة ما لتضييق النوع تلقائيًا على get ليكون من النوع الذي أعرف أنه سيكون دائمًا.

لا أفهم لماذا يعترض البعض على أنه ينتهك أمان النوع وكل شيء. لا أرى أنه ينتهك أي نوع من الأمان إذا سمحنا على الأقل للمُعد بأن يكون له نوع مختلف ، لأنه بأي طريقة لدينا تحقق في المكان لن تسمح لنوع آخر بتعيينه على خاصية ما.
السابق:
لنفترض أن لدي خاصية مثل Arrayولكن من DB سيتم إرجاعها كسلسلة مع فصل فاصلات مثل قل "10، 20،40". لكن لا يمكنني تعيين ذلك لخاصية الوضع الآن ، لذا سيكون من المفيد جدًا السماح بإعجاب

قائمة _ EmployeeId الخاصة: الرقم []

الحصول على EmployeeIDList (): الرقم [] {
إعادة هذه.
}
تعيين EmployeeIDList (_idList: أي ) {
إذا (typeof _idList == 'سلسلة') {
this._employeeIdList = _idList.split ('،'). map (d => Number (d)) ؛
}
else if (typeof _idList == 'object') {
this._employeeIdList = _idList كرقم [] ؛
}
}

كنت سأحل هذه المشكلة بسهولة وهي من النوع الآمن تمامًا ، على الرغم من أنك تسمح بنوع مختلف في SET ولكن لا يزال يمنعنا من تعيين نوع خاطئ للممتلكات. حتى الفوز. آمل أن يتخلى أعضاء الفريق عن غرورهم وأن يحاولوا التفكير في المشكلة التي يخلقها ويصلحوا هذا الأمر.

أرغب في التناغم مع أنني ما زلت أعتقد أن هذا مهم حقًا لنمذجة سلوك مكتبات JS الحالية.

في حين أنه من الجيد والجيد رفع الأنوف وتحديد محدد يقوم بإجبار نوع على مجموعة فرعية من النوع الوارد ويعيد دائمًا تلك المجموعة الفرعية في المُجمع كرائحة كود ، في الواقع ، هذا نمط شائع بشكل معقول في JS land .

أعتقد أن TypeScript جميل لأنه يتيح لنا أن نكون معبرة جدًا عند وصف cagaries لواجهات برمجة تطبيقات JS الحالية ، حتى لو لم يكن لديهم واجهات برمجة تطبيقات منظمة بشكل مثالي. أعتقد أن هذا سيناريو حيث إذا كان TypeScript معبرًا بدرجة كافية للسماح لنا بالإشارة إلى أن getter سيعيد دائمًا نوعًا يمثل مجموعة فرعية صارمة من نوع getter ، فسيضيف هذا قدرًا هائلاً من القيمة في نمذجة واجهات برمجة التطبيقات الحالية ، حتى DOM!

Trusted Types هو اقتراح جديد لواجهة برمجة تطبيقات المتصفح لمحاربة DOM XSS. تم تنفيذه بالفعل في Chromium (خلف العلم). يقوم الجزء الأكبر من واجهة برمجة التطبيقات بتعديل محددات خصائص DOM المختلفة لقبول الأنواع الموثوقة. على سبيل المثال ، يقبل $ .innerHTML TrustedHTML | string بينما يُرجع دائمًا string . لوصف API في TypeScript ، سنحتاج إلى إصلاح هذه المشكلة.

يتمثل الاختلاف عن التعليقات السابقة في أن هذه واجهة برمجة تطبيقات للمتصفح (وليست مكتبة لأرض المستخدم) والتي لا يمكن تغييرها بسهولة. كما أن تأثير تغيير نوع Element.innerHTML إلى any (وهو الحل الوحيد الممكن حاليًا) أكبر من الوصف غير الدقيق لمكتبة أرض المستخدم.

هل هناك فرصة لإعادة فتح طلب السحب هذا؟ أم أن هناك حلول أخرى فاتني؟

نسخة إلى: mprobst ،koto.

في اللغة التي تدعم اتحاد الكتابة مثل TypeScript ، تعد هذه الميزة طبيعية ونقطة بيع.

RyanCavanaugh حتى عندما يكون لكل من getter و setter نفس النوع ، فليس من المضمون أن o.x === y بعد o.x = y حيث أن المُعد قد يقوم ببعض التعقيم قبل حفظ القيمة.

element.scrollTop = -100;
element.scrollTop; // returns 0

أنا ثاني القلق منvrana. يجعل السلوك الحالي من المستحيل نمذجة بعض واجهات برمجة التطبيقات الموجودة في Typescript.

ينطبق هذا بشكل خاص على واجهات برمجة تطبيقات الويب ، حيث تختلف أنواع الواضع والجامع. من الناحية العملية ، يقوم واضعو واجهة برمجة تطبيقات الويب بتنفيذ جميع أنواع الإكراه ، وبعضها محدد مباشرة لميزة مستعرض معينة ، ولكن معظمها ضمنيًا عبر IDL . يقوم الكثير من المحددات بتغيير القيمة أيضًا ، راجع على سبيل المثال مواصفات واجهة الموقع . هذا ليس خطأ مطور واحد - إنها مواصفات لواجهة برمجة التطبيقات التي يرمز مطورو الويب ضدها.

يسمح تضييق أنواع getter لـ Typescript بتمثيل واجهات برمجة التطبيقات ، وهو أمر مستحيل الآن.

يمكنك تمثيل واجهات برمجة التطبيقات هذه لأنه من الصحيح القول إن نوع الخاصية هو اتحاد الأنواع الممكنة التي يمكنك توفيرها للمُعد أو الحصول عليها من المُحضر.

إن وصف واجهة برمجة التطبيقات بهذه الطريقة ليس _فعالًا. أنت تطلب من المستهلك استخدام نوع حارس في كل حالة يستخدم فيها برنامج getter لتضييق الأنواع الممكنة.

لا بأس بذلك إذا لم تكن قد جعلت الأمر بديهيًا أنك ستعيد دائمًا نوعًا ضيقًا من برنامج جامع ، حيث أن العديد من واجهات برمجة تطبيقات الويب يتم قفلها في مواصفاتها.

ولكن حتى لو وضعنا ذلك جانبًا للحظة ، والتحدث عن واجهات برمجة تطبيقات المستخدم ، فإن حالة الاستخدام القوية لقبول أنواع الاتحاد على أداة ضبط هي الحالة "النصية". نريد قبول مجموعة من الأنواع المنفصلة التي يمكننا إجبارها بشكل مقبول على النوع الذي نريده بالفعل.

لماذا تسمح بذلك؟ سهولة الاستعمال.

قد لا يكون ذلك مهمًا لواجهات برمجة التطبيقات التي تقوم بتطويرها داخليًا لاستخدام فريقك ، ولكن يمكن أن يكون مهمًا جدًا لواجهات برمجة التطبيقات المصممة للاستهلاك العام والمعمم.

من الرائع أن تسمح لنا TypeScript بوصف واجهة برمجة التطبيقات (API) التي خففت من الأنواع المقبولة لبعض خصائصها بدقة ، ولكن هذه الميزة تشوبها الاحتكاك على الطرف الآخر حيث يلزم إجراء فحص / حماية مفرط للنوع لتحديد نوع إرجاع المُحضر. ، والتي نفضل _specify_.

كنت أزعم أن هذا سيناريو مختلف عن الحالة المثالية التي تفترضهاRyanCavanaugh . تشير هذه الحالة إلى أنه يجب أن يكون لديك كل من getter و setter دائمًا نفس نوع الاتحاد لأن حقل الدعم الخاص بك يحتوي أيضًا على نفس نوع الاتحاد ، وستقوم دائمًا برحلة ذهابًا وإيابًا فقط لتلك القيمة ، وتغيير الكتابة أمر غير منطقي.

أعتقد أن هذه الحالة تتمحور حول استخدام أكثر مثالية لأنواع الاتحاد ، حيث تتعامل مع أنواع مبنية وتعامل حقًا هذا النوع من الاتحاد كوحدة دلالية ، والتي ربما كان من المفترض أن تنشئ اسمًا مستعارًا لها.

type urlRep = string | Url;

ستقوم معظم الأشياء برحلة ذهابًا وإيابًا ، والتعامل مع الدعائم الشائعة فقط ، وفي بعض الحالات ستكسر الصندوق الأسود ببعض حراس الكتابة.

أنا أزعم أن هذا سيناريو مختلف تمامًا عما نصفه هنا. ما نصفه هو حقيقة أن واجهات برمجة التطبيقات للاستخدام العام / العام ، خاصة للاستخدام في لغات البرمجة النصية مثل جافا سكريبت ، غالبًا ما تخفف عمداً الأنواع المقبولة للمُعد ، نظرًا لوجود مجموعة من الأنواع التي يوافقون على إجبارها على النوع المثالي ، لذلك يقدمونها كنوع من تحسين جودة الحياة لقبول كل هؤلاء.

قد يبدو هذا النوع من الأشياء غير منطقي إذا كنت منتجًا ومستهلكًا لواجهة برمجة التطبيقات (API) ، لأنه يؤدي فقط إلى مزيد من العمل ، ولكن يمكن أن يكون له معنى كبير إذا كنت تصمم واجهة برمجة تطبيقات للاستهلاك الشامل.

ولا أعتقد أن أي شخص قد يرغب أبدًا في أن يمتلك الواضع / الجامع أنواع _ انفصال_. ما تتم مناقشته هنا هو أن مُنتِج واجهة برمجة التطبيقات يؤكد للمستهلك أن المُحضر سيعيد قيمة بنوع يمثل مجموعة فرعية صارمة من نوع الاتحاد للمُعيِّن.

ولا أعتقد أن أي شخص يريد أبدًا أن يكون لدى الواضع / الجامع أنواع منفصلة.

فقط لتقديم وجهة نظر مضادة لذلك. أود بالتأكيد ذلك لأن وجود أنواع منفصلة لـ setter / getter أمر قانوني تمامًا في جافا سكريبت. وأعتقد أن الهدف الأساسي يجب أن يكون جعل نظام الطباعة معبرًا بما يكفي لكتابة أي شيء قانوني في جافا سكريبت بشكل صحيح (على الأقل على مستوى .d.ts). أتفهم أنها ليست ممارسة جيدة ، ولا كائنات عالمية على سبيل المثال ، أو تغيير النموذج الأولي للبناءات مثل الوظيفة وما إلى ذلك ، ومع ذلك ما زلنا قادرين على كتابة تلك الأشياء في ملفات .d.ts ولم أسمع أي شخص نادمًا على ذلك يمكننا القيام بذلك (على الرغم من أنه يتسبب في ظهور بعض ملفات تعريف النوع سيئة التصميم عند كتابتها بالتأكيد).

واجهت للتو موقفًا أحتاج فيه إلى أن يكون هذا ميزة من أجل الحصول على الأنواع الصحيحة التي لا أتحكم فيها. يحتاج الواضع إلى أن يكون أكثر ليبرالية وإجبارًا على نوع أكثر تحفظًا والذي يمكن أن يعيده الجامع باستمرار.

أنا حقا أتمنى أن تكون هذه الميزة موجودة. أنا أقوم بنقل كود JavaScript إلى TypeScript ، ومن المشكله الحقيقية أنه سيتعين علي إعادة تشكيل عوامل التعيين إذا أردت أمان الكتابة.

على سبيل المثال ، لدي شيء مثل هذا:

class Vector3 { /* ... */ }

type XYZ = Vector3 | [number, number, number] | {x: number, y: number, z: number}
type PropertyAnimator = (x: number, y: number, z: number, timestamp: number) => XYZ
type XYZSettables =  XYZ | PropertyAnimator

export class Transformable {
        // ...

        set position(newValue: XYZSettables) {
            this._setPropertyXYZ('position', newValue)
        }
        get position(): Vector3 {
            return this._props.position
        }

        // ...
}

وكما يمكنك أن تتخيل ، فإن الاستخدام مرن للغاية:

const transform = new Transformable

// use an array
transform.position = [20, 30, 40]

// use an object
transform.position = {y: 30, z: 40} // skip `x` this time

// animate manually, a property directly
requestAnimationFrame((time) => {
  transform.position.x = 100 * Math.sin(time * 0.001)
})

// animate manually, with an array, which could be shared across instances
const pos = [10, 20, 30]
requestAnimationFrame((time) => {
  pos[2] = 100 * Math.sin(time * 0.001)
  transform.position = pos
})

// Animate with a property function
transform.position = (x, y, z, time) => [x, y, 100 * Math.sin(time * 0.001)]

// or a simple increment:
transform.position = (x, y, z) => [x, y, ++z]

// etc

// etc

// etc

هذا عمره 4 سنوات. كيف لا يزال غير ثابت؟ هذه الميزة ، كما هو مذكور في التعليقات أعلاه ، مهمة! على الأقل التفكير في إعادة فتح القضية؟

وأنا أتفق تمامًا مع kitsonk :

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

قدم TypeScript 3.6 بناء جملة لكتابة الموصلات في ملفات التصريح ، مع الاحتفاظ بالقيود التي تنص على أن نوع getter و setter يجب أن يكونا متطابقين.

تعتمد مجموعة أدوات واجهة المستخدم الخاصة بنا بشكل كبير على أدوات ضبط الصب التلقائي ، حيث يقبل الواضع أنواعًا أكثر من النوع المفرد الذي يتم إرجاعه بواسطة أداة الإرجاع. لذلك سيكون من الرائع أن تقدم TS طريقة لجعل هذا النمط آمنًا.

يبدو أن RyanCavanaugh قدم دحضًا ملموسًا لهذه الميزة منذ 3 سنوات . أتساءل عما إذا كانت حالات استخدام DOM التي تم الإبلاغ عنها مؤخرًا بالإضافة إلى توفر صياغة إعلان جديدة قد تسمح باتخاذ قرار جديد؟

نعم ، بمجرد أن رأيت هذه الميزة الجديدة ، فكرت على الفور في طلب الميزة هذا أيضًا. أريده أيضًا لأغراض أطر عمل واجهة المستخدم الخاصة بنا. هذه حالة استخدام حقيقية يا شباب.

سيعقد TSConf 2019 في 11 أكتوبر ، معتقدًا أن هذا سيكون بمثابة استرجاع رائع في جلسة الأسئلة والأجوبة ، إذا حصل شخص ما على فرصة 🤔

ربما قلت هذا من قبل في هذا الموضوع الطويل ، لكنني أعتقد أنه يستحق التكرار.

IMO يخدم نظام الكتابة التعبيرية في TypeScript غرضين منفصلين. الأول هو السماح لك بكتابة رمز جديد أكثر أمانًا. وإذا كان هذا هو الغرض الوحيد ، فربما يمكنك تقديم حجة مفادها أنه يمكنك تجنب حالة الاستخدام هذه لأن الكود قد يكون أكثر أمانًا إذا رفضت هذا السيناريو (لكنني أعتقد أنه لا يزال بإمكاننا الخوض في جدال حول هذا).

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

على الأقل ، أعتقد أنه يمكن إزالة Design Limitation هنا ، ولا أعتقد أن هذا ينطبق بعد الآن.

من الواضح أن هذا هو السبب في حقيقة أن لكل # 33749 .style.display = ...something nullable... لم يعد يقوم بفحص الكتابة ؛ التي يتم تقديمها على أنها مسألة صحة بطلان. هذا مخادع بعض الشيء ، أليس كذلك؟ إنه لأمر مزعج أن تضطر إلى اكتشاف تغييرات جديدة عندما يتم تصنيفها بشكل خاطئ على أنها إصلاحات أخطاء (بحثت عن المشكلات الفعلية التي ربما تسبب هذا في حدوثها). أنا شخصياً أجد أنه من غير المفاجئ أن يكون لدى null سلوك خاص "use default" ، مقارنة بالسلسلة الفارغة ؛ وحتى typecipt 3.7 فضلت استخدام null لالتقاط ذلك. في أي حال ، سيكون من الجيد أن يتم وضع تعليقات توضيحية من النوع غير الصحيح عن عمد لحل هذا القيد بشكل واضح على هذا النحو ، لتوفير الوقت في ترتيب مشكلات الترقية.

أنا مهتم أيضًا بإيجاد طريق للمضي قدمًا من أجل ذلك. ماذا لو تم السماح به فقط في السياقات المحيطة؟ RyanCavanaugh هل سيساعد ذلك في معالجة مخاوفك المعقدة؟

حالة الاستخدام الخاصة بي لهذا الأمر هي أن لدي واجهة برمجة تطبيقات حيث يعرض الوكيل وعدًا ، لكن العملية المحددة لا تقدم وعدًا. لا يمكنني وصف هذا في TypeScript الآن.

let post = await loadPost()
let user = await loadUser()
post.author = user // Proxy handles links these two objects via remote IDs
await save(post)

// Somewhere else in code
let post = await loadPost()
let author = await post.author

سواء أكان النوع يصحح الوظائف الأصلية ، أو الوكلاء بشكل عام ، يبدو أن TypeScript يبدو أن هذه الأنواع من الميزات كانت خطأ.

يجب أن يأخذوا رقم 6 ورقم 7 من هذه القائمة (https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#goals)

مشكلتي

أرغب في دفع عنصر إلى مصفوفة في طريقة المجموعة والحصول على المصفوفة بأكملها في طريقة get. في الوقت الحالي ، سيتعين علي استخدام set myArray(...value: string[]) والذي يعمل بشكل جيد. تصطدمت بهذه المشكلة كثيرًا ... يرجى التفكير في إزالة هذا. سوف تعمل المعلومات أو التحذير بشكل جيد لهذا الغرض.

مثال (ماذا أود أن أفعل)

class MyClass {
   _myArray: string[] = [];

   set myArray(value: string) {
      this._myArray.push(value);
   }

   get myArray(): string[] {
      return this._myArray;
   }
}

مثال (ما علي فعله)

class MyClass {
   _myArray: string[] = [];

   set myArray(...value: string[]) {
      this._myArray.push(value[0]);
   }

   get myArray(): string[] {
      return this._myArray;
   }
}

أحد الحلول التي توصلت إليها عندما كنت بحاجة إلى هذا هو تعيين الخاصية لتكون union type . هذا لأن وقتي يأتي كسلسلة من واجهة برمجة التطبيقات ، لكني أحتاج إلى التعيين ككائن لحظة لواجهة المستخدم الخاصة بي.

المثال الخاص بي

class TimeStorage {
    _startDate: string | Moment = ""
    _endDate: string | Moment = ""

    set startDate(date: Moment | string) {
        this._startDate = moment(date).utc()
    }
    get startDate(): Moment | string {
        return moment.utc(this._startDate)
    }

    set endDate(date: Moment) { 
        this._endDate = moment.utc(date)
    }
    get endDate() { 
        return moment.utc(this._endDate)
    }
}

لقد تأخرت قليلاً عن الحفلة لكنني واجهت هذه المشكلة مؤخرًا بنفسي. إنها معضلة مثيرة للاهتمام لذا فقد تلاعبت بها قليلاً.

لنفترض أن هذا شيء نريد صنعه ، فئة بسيطة لها خاصية تقبل السلاسل والأرقام (لأي غرض) ولكن الخاصية مخزنة بالفعل كسلسلة:

class Foo {
    constructor() {
        this._bar = '';
        this.baz = '';
    }

    // error: 'get' and 'set' accessor must have the same type.(2380)
    get bar(): string {
        return this._bar;
    }

    // error: 'get' and 'set' accessor must have the same type.(2380)
    set bar(value: string | number) {
        this._bar = value.toString();
    }

    public baz: string;
    private _bar: string;
}

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

/**
 * Abstract base class for properties.
 */
abstract class Property<GetType, SetType> {
    abstract get(): GetType;
    abstract set(value: SetType): void;
}

/**
 * Proxify an object so that it's get and set accessors are proxied to
 * the corresponding Property `get` and `set` calls.
 */
function proxify<T extends object>(obj: T) {
    return new Proxy<any>(obj, {
        get(target, key) {
            const prop = target[key];
            return (prop instanceof Property) ? prop.get() : prop;
        },
        set(target, key, value) {
            const prop = target[key];
            if (prop instanceof Property) {
                prop.set(value);
            } else {
                target[key] = value;
            }
            return true;
        }
    });
}

باستخدام هذه المكتبة ، يمكننا تنفيذ خاصيتنا المكتوبة لفصلنا مثل هذا:

class Bar extends Property<string, string | number> {
    constructor(bar: string | number) {
        super();
        this.set(bar);
    }

    get(): string {
        return this._bar;
    }

    set(value: string | number) {
        this._bar = value.toString();
    }

    private _bar: string;
}

class Foo {
    constructor() {
        this.bar = new Bar('');
        this.baz = '';
    }

    public bar: Bar;
    public baz: string;
}

وبعد ذلك ، فإن استخدام هذه الفئة سيكون له النوع الآمن getter and setter مقابل bar :

const foo = new Foo();

// use property's typed setter
foo.bar.set(42);
foo.baz = 'foobar';

// use property's typed getter
// output: 42 foobar
console.log(foo.bar.get(), foo.baz);

وإذا كنت بحاجة إلى استخدام محددات الخاصية أو محصليها بشكل ديناميكي ، فيمكنك استخدام الدالة proxify للالتفاف:

const foo = new Foo();

// use proxified property setters
Object.assign(proxify(foo), { bar: 100, baz: 'hello world' });

// use property's typed getter
// output: 100 hello world
console.log(foo.bar.get(), foo.baz);

// use proxified property getters
// output: {"bar":"100","baz":"hello world"}
console.log(JSON.stringify(proxify(foo)));

إخلاء المسؤولية: إذا أراد شخص ما أخذ هذا الرمز ووضعه في مكتبة أو فعل ما يشاء به ، فلا تتردد في القيام بذلك. أنا كسول جدا.

فقط بضع ملاحظات إضافية وبعد ذلك أعدك بأنني سأتوقف عن إرسال رسائل غير مرغوب فيها إلى هذه القائمة الطويلة من الأشخاص!

إذا أضفنا هذا إلى المكتبة التخيلية:

/**
 * Create a property with custom `get` and `set` accessors.
 */
function property<GetType, SetType>(property: {
    get: () => GetType,
    set: (value: SetType) => void
}) {
    const obj = { ...property };
    Object.setPrototypeOf(obj, Property.prototype);
    return obj;
}

نحصل على تطبيق أقرب قليلاً إلى الفصل الأصلي ، والأهم من ذلك أنه يمكنه الوصول إلى this :

class Foo {
    constructor() {
        this._bar = '';
        this.baz = '';
    }

    public bar = property({
        get: (): string => {
            return this._bar;
        },
        set: (value: string | number) => {
            this._bar = value.toString();
        }
    });

    public baz: string;
    private _bar: string;
}

إنه ليس أجمل شيء في العالم ولكنه يعمل. ربما يمكن لشخص ما أن يصقل هذا وأن يصنع شيئًا لطيفًا بالفعل.

في حالة عدم اقتناع أي فرد يقرأ هذا الموضوع بأن هذه الميزة مهمة ، وهو أمر غير مرجح ، أقدم لك هذا الاختراق القبيح بشكل لا يصدق الذي قدمه Angular للتو :

سيكون من المثالي تغيير نوع القيمة هنا ، من قيمة منطقية إلى قيمة منطقية | '' ، لتتناسب مع مجموعة القيم التي يتم قبولها فعليًا من قبل الواضع. تتطلب TypeScript أن يكون لكل من getter و setter نفس النوع ، لذلك إذا كان يجب على getter إرجاع قيمة منطقية ، فسيتم تعليق أداة الإعداد مع النوع الأضيق .... يدعم Angular التحقق من نوع أوسع وأكثر تساهلاً لـ Input () مما هو عليه أعلن عن حقل الإدخال نفسه. قم بتمكين هذا عن طريق إضافة خاصية ثابتة مع البادئة ngAcceptInputType_ إلى فئة المكون:

""
فئة إرسال الزر {
_disabled خاص: منطقي ؛

الحصول على تعطيل (): منطقي {
إرجاع هذا ._ معطل ؛
}

تعيين معطل (القيمة: منطقية) {
this._disabled = (القيمة === '') || القيمة؛
}

ثابت ngAcceptInputType_disabled: منطقي | '' ؛
}
""

من فضلك ، من فضلك أصلح هذا.

نعم ، لدينا هذا القبح في كل مكان لدعم الأنماط التي يريد Angular استخدامها. التقييد الحالي شديد العزم.

إذا أرادت TS استخدامها لنمذجة النطاق الكامل للمكتبات في أرض الويب ، فيجب أن تسعى جاهدة لتكون أقل رأيًا ، أو ستفشل في تصميم جميع التصميمات.

في هذه المرحلة ، أعتقد أن المسؤولية تقع على عاتق المساهمين للتعبير عن سبب عدم إعادة فتح هذه المشكلة؟ هل هناك عضو مساهم يعارض ذلك بشدة في هذه المرحلة؟

نحن بصدد إنشاء مكتبة DOM لـ NodeJs تتطابق مع مواصفات W3C ، ولكن مشكلة التنقيح هذه تجعل ذلك مستحيلًا. أي تحديث؟

إنه لأمر صادم بالنسبة لي أن البعض داخل الفريق الأساسي في TypeScript يعارض ميزة مطلوبة لإعادة إنشاء واحدة من مكتبات JavaScript الأكثر استخدامًا على هذا الكوكب: DOM.

لا توجد طريقة أخرى لأ) تنفيذ أجزاء كثيرة من مواصفات W3C DOM أثناء ب) باستخدام نظام كتابة TypeScript (إلا إذا كنت تلجأ إلى استخدام any في كل مكان ، والذي يتعارض مع الغرض الكامل من TypeScript).

الصفحة الرئيسية لـ typescriptlang.org حاليًا غير دقيقة عندما تنص على:

TypeScript هو مجموعة فرعية مكتوبة من JavaScript يتم تجميعها إلى JavaScript عادي.

تظهر عدم القدرة على إعادة إنشاء مواصفات DOM الخاصة بجافا سكريبت أن TypeScript لا تزال مجموعة فرعية من Javascript وليس مجموعة فرعية .

فيما يتعلق بتنفيذ هذه الميزة ، أتفق مع @ gmurray81 والعديد من الأشخاص الآخرين الذين جادلوا بأن نوع getter يجب أن يكون مجموعة فرعية من نوع اتحاد المستوطنين. هذا يضمن عدم وجود كتابة مفككة. يسمح هذا الأسلوب لوظيفة المُعيِّن بتنظيف المدخلات دون إتلاف نوع الحاصل (على سبيل المثال ، إجبارك على اللجوء إلى استخدام any ).

هذا ما لا يمكنني فعله. شيء بسيط فعلته مشابهًا في JS لكن لا يمكنني ذلك في TS.

قضية 4yo التي يمكن تنفيذها حقًا .

export const enum Conns {
  none = 0, d = 1, u = 2, ud = 3,
  r = 4, rd = 5, ru = 6, rud = 7,
  l = 8, ld = 9, lu = 10, lud = 11,
  lr = 12, lrd = 13, lru = 14, lrud = 15,
  total = 16
}

class  Tile {
  public connections = Conns.none;

  get connLeft() { return this.connections & Conns.l; };

  set connLeft(val: boolean) { this.connections = val ? (this.connections | Conns.l) : (this.connections & ~Conns.l); }
}

يجري في هذه القضية الآن. ما يلي يبدو كحل معقول ، على سبيل المثالcalebjclark :

فيما يتعلق بتنفيذ هذه الميزة ، أتفق مع @ gmurray81 والعديد من الأشخاص الآخرين الذين جادلوا بأن نوع getter يجب أن يكون مجموعة فرعية من نوع اتحاد المستوطنين. هذا يضمن عدم وجود كتابة مفككة. يسمح هذا الأسلوب لوظيفة المُعيِّن بتنظيف المدخلات دون إتلاف نوع أداة الضبط (أي ، إجبارها على اللجوء إلى استخدام أيٍّ منها).

بالنسبة لكون الحاصل مجموعة فرعية من نوع الواضع ، أعتقد أن هذا محدود بلا داع. حقًا ، يمكن أن يكون نوع المُعيِّن _could_ نظريًا أي شيء حرفيًا ، طالما أن التنفيذ يُرجع دائمًا نوعًا فرعيًا من نوع getter. إن اشتراط أن يكون الواضع نوعًا فائقًا من نوع getter هو نوع من التقييد الغريب الذي قد يفي بحالتين من حالات الاستخدام المقدمة في هذه البطاقة ، ولكن من المؤكد أنه سيتلقى شكاوى لاحقًا.

ومع ذلك ، فإن الفئات هي ضمنيًا واجهات أيضًا ، حيث تكون الحاصلون والمحددون في الأساس تفاصيل تنفيذ لا تظهر في الواجهة. في مثال OP ، سينتهي بك الأمر بـ type MyClass = { myDate(): moment.Moment; } . سيتعين عليك بطريقة أو بأخرى الكشف عن هذه الأنواع الجديدة من برامج getter و setter كجزء من الواجهة ، على الرغم من أنني لا أرى شخصيًا سبب كون ذلك مرغوبًا فيه.

تطالب هذه المشكلة بشكل أساسي بإصدار أقل محدودية من التحميل الزائد للمشغل = و === . (يعتبر المستحوذون والمُعيِّنون بالفعل عبئًا زائدًا على المشغل.) وباعتباري شخصًا تعامل مع العبء الزائد للمشغل بلغات أخرى ، سأقول إنني أشعر أنه يجب تجنبها في معظم الحالات. بالتأكيد ، يمكن لشخص ما أن يقترح بعض الأمثلة الرياضية مثل النقاط الديكارتية والقطبية ، ولكن في الغالبية العظمى من الحالات التي تستخدم فيها TS (بما في ذلك الأمثلة في هذا الموضوع) ، أود أن أزعم أن الحاجة إلى التحميل الزائد للمشغل هي على الأرجح رائحة رمز. قد يبدو الأمر وكأنه يجعل API أبسط ، لكنه يميل إلى القيام بالعكس. كما ذكرنا سابقًا ، فإن وجود ما يلي ليس بالضرورة صحيحًا أمر مربك للغاية وغير بديهي.

foo.x = y;
if (foo.x === y) { // this could be false?

(أعتقد أن المرات الوحيدة التي رأيت فيها أدوات ضبط بدت معقولة هي لتسجيل الدخول ووضع علامة "قذرة" على الكائنات بحيث يمكن لشيء آخر معرفة ما إذا كان الحقل قد تم تغييره أم لا. لا يغير أي من هذين الحقين عقد هدف.)

تضمين التغريدة

[...] ولكن في الغالبية العظمى من الحالات التي تستخدم فيها TS (بما في ذلك الأمثلة في هذا الموضوع) ، أود أن أزعم أن الحاجة إلى التحميل الزائد للمشغل ربما تكون رائحة كود [...]

 foo.x = y; 
 if (foo.x === y) { // this could be false?

لذا ، فإن مجرد وجود الأنواع المتطابقة لا يمنع السلوك المفاجئ ، وبالفعل يظهر el.style.display = ''; //so is this now true: el.style.display === ''? أن هذا ليس نظريًا أيضًا. حتى مع الحقول القديمة البسيطة ، فإن الافتراض لا ينطبق على NaN ، بالمناسبة.

ولكن الأهم من ذلك ، أن حجتك تتجاهل النقطة التي مفادها أن TS لا يمكن إبداء آرائها حول هذه الأشياء لأن TS يحتاج إلى التداخل مع JS apis الحالي ، بما في ذلك الأساسيات التي من غير المرجح أن تغير أشياء مثل DOM. وعلى هذا النحو ، لا يهم ما إذا كانت واجهة برمجة التطبيقات مثالية أم لا ؛ ما يهم هو أن TS لا يمكنها التعامل بشكل نظيف مع واجهات برمجة تطبيقات معينة . الآن ، أنت مضطر إما إلى إعادة تنفيذ أي منطق احتياطي تستخدمه واجهة برمجة التطبيقات داخليًا لإجبار قيمة خارجة عن الحصول على قيمة تم تمريرها إلى أداة الإعداد ، وبالتالي كتابة الخاصية كنوع الحاصل ، أو تجاهل التحقق من النوع للإضافة تأكيدات نوع عديمة الفائدة في كل موقع من مواقع الحصول ، ومن ثم اكتب الخاصية كنوع المحدد. أو الأسوأ من ذلك: افعل أيًا كان ما يحدث لـ TS لاختيار إضافة تعليق توضيحي لـ DOM ، بغض النظر عما إذا كان ذلك مثاليًا.

كل هذه الخيارات سيئة. الطريقة الوحيدة لتجنب ذلك هي قبول الحاجة إلى تمثيل JS apis بأمانة قدر الإمكان ، وهنا: يبدو ذلك ممكنًا (من المسلم به أنه بدون أي معرفة بأجزاء TS الداخلية التي قد تجعل هذا غير تافه).

يبدو ذلك ممكنًا (من المسلم به دون أي معرفة بأجزاء TS الداخلية التي قد تجعل هذا بالتأكيد غير تافه)

أظن أن بعض بناء جملة إعلان النوع الجديد الذي قاموا بتمكينه مؤخرًا يمكن أن يجعل هذا التعبير ممكنًا عندما كان أقل جدوى من قبل ، لذلك يجب عليهم التفكير في إعادة فتح هذه المشكلة ... RyanCavanaugh

في عالم مثالي ، سيتفاعل TS مع جميع واجهات برمجة تطبيقات JS. لكن ليست هذه هي المسألة. هناك مجموعة من مصطلحات JS لا يمكن كتابتها بشكل صحيح في TS. ومع ذلك ، أفضل تركيزهم على إصلاح الأشياء التي تؤدي في الواقع إلى مصطلحات برمجة أفضل ، وليس أسوأ. في حين أن دعم هذا بطريقة عاقلة سيكون أمرًا لطيفًا ، إلا أن هناك عشرات الأشياء الأخرى التي أفضلها شخصيًا لقضاء وقتهم المحدود في البداية. هذا هو الطريق إلى أسفل على تلك القائمة.

MikeyBurkman سؤال الأولوية صحيح. شاغلي الأساسي هو قرار RyanCavanaugh بإغلاق هذه المشكلة وشطبها. يتعارض استبعاد جميع المشكلات المشار إليها في هذا الموضوع بشكل مباشر مع مهمة Typescript المعلنة ، والتي تتمثل في أن تكون "مجموعة شاملة" من Javascript (بدلاً من مجموعة فرعية).

نعم ، يمكنني بالتأكيد أن أوافق على أنه ربما لا ينبغي إغلاق هذه المشكلة ، لأنها شيء ربما يجب إصلاحه في النهاية. (على الرغم من أنني أشك في ذلك حقًا).

في حين أن دعم هذا بطريقة عاقلة سيكون أمرًا لطيفًا ، إلا أن هناك عشرات الأشياء الأخرى التي أفضلها شخصيًا لقضاء وقتهم المحدود في البداية

أعتقد أنك تبيع جانبًا مهمًا من TypeScript من حيث أنه من المفترض أن تجعل استخدام واجهات برمجة تطبيقات DOM الحالية وواجهات برمجة تطبيقات JS الحالية أكثر أمانًا. إنه موجود للاحتفاظ بأكثر من مجرد كود خاص بك ، داخل فقاعته ، آمن.

هذه هي وجهة نظري ، أنا أقوم ببناء مكتبات مكونة ، وعلى الرغم من أنني لن أتسبب بالضرورة عن عمد في التسبب في عدم التطابق بين الواقفين / الحاصلين على هذه العناصر. أحيانًا يتم فرض يدي بناءً على كيفية احتياج مكتباتي للتفاعل مع أنظمة أخرى في مكانها. على سبيل المثال ، يعيّن Angular جميع خصائص الإدخال على أحد المكونات القادمة من الترميز كسلاسل ، بدلاً من القيام بأي نوع من أنواع الإكراه بناءً على معرفتهم بنوع الهدف (على الأقل ، آخر مرة راجعت فيها). لذا ، هل ترفض قبول القيود؟ هل تجعل كل شيء خيطًا ، على الرغم من أن هذا من شأنه أن يجعل استخدام أنواعك سيئًا؟ أو تفعل ما تريده TypeScript وتستخدم نوعًا مثل: string | لون ولكن استخدام حاصل بشعة. هذه كلها خيارات رهيبة جدًا تقلل من الأمان ، في حين أن بعض التعبيرات الإضافية من نظام الكتابة كان من الممكن أن تساعد.

المشكلة هي أن Angular ليس فقط هو الذي يسبب هذه المشكلات. انتهى الأمر بالزاوية في هذا الموقف لأنها تعكس الكثير من السيناريوهات في DOM حيث يحدث الإكراه التلقائي على مجموعة خصائص ، لكن المُحضر دائمًا نوع فردي متوقع.

انظر قليلاً إلى الأعلى: Angular أسوأ بكثير مما تعتقد .

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

class Store {
  private dict: Map<string, any>;

  get name(): string | null {
    return this.dict.get('name') as string | null;
  }

  set name(value: string) {
    this.dict.set('name', value);
  }
}

تريد أن يكون لديك مثل هذا القيد: يمكن للمستخدم الحصول على null ، إذا لم يتم تعيين القيمة مسبقًا ، ولكن لا يمكن تعيينها على null . حاليًا ، لا يمكنك القيام بذلك ، لأن نوع إدخال المُعيِّن يجب أن يتضمن null .

@ fan-tom ، حالة استخدام رائعة لسبب الحاجة إلى إعادة فتح هذه المشكلة! إبقاء م القادمة.

هذه القضية لديها عدد كبير للغاية من الأصوات المؤيدة!

هذا هو مشروع فريق TS ، لذا يمكنهم أن يفعلوا ما يرونه مناسبًا كيفما يستمتعون. ولكن إذا كانوا يهدفون إلى جعل هذا مفيدًا قدر الإمكان لمجتمع خارجي من المستخدمين ، فعندئذ آمل أن يتمكن فريق TS من أخذ العدد الكبير من أصوات المجتمع في الاعتبار بقوة.

الصفحة الرئيسية لـ typescriptlang.org حاليًا غير دقيقة عندما تنص على:

TypeScript هو مجموعة فرعية مكتوبة من JavaScript يتم تجميعها إلى JavaScript عادي.

TypeScript هي مجموعة فرعية مكتوبة من مجموعة فرعية من JavaScript يتم تجميعها إلى JavaScript عادي. : مبتسم:

TypeScript هو مجموعة فرعية مكتوبة لمجموعة فرعية من JavaScript يتم تجميعها إلى JavaScript عادي.

بشكل أكثر دبلوماسية ، أعتقد أنه يمكنك القول إنها مجموعة شاملة من JavaScript برأي ، عندما يقوم الفريق بهذا النوع من الحذف المتعمد من نمذجة نظام الكتابة.

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

هذا هو السبب في أنه من الغريب جدًا رفض نموذج هذا السيناريو الشائع (المستخدم في DOM!) ، مشيرًا إلى الاعتقاد بأن واجهات برمجة التطبيقات (API) لا ينبغي أن تقوم بالإكراه القائم على المحدد كأساس منطقي لعدم القيام بذلك.

حاليًا ، يبدو أن هذا غير ممكن ، ولا بد لي من اللجوء إلى شيء مثل هذا:

class MyClass {

    private _myDate: moment.Moment;

    get myDate(): moment.Moment {
        return this._myDate;
    }

    set myDate(value: moment.Moment) {
        assert.fail('Setter for myDate is not available. Please use: setMyDate() instead');
    }

    setMyDate(value: Date | moment.Moment) {
        this._myDate = moment(value);
    }
}

أفضل ما يمكنك فعله هو إضافة مُنشئ واستدعاء وظيفة المُعيِّن المخصص هناك ، إذا كانت هذه هي المرة الوحيدة التي تُعيِّن فيها هذه القيمة.

constructor(value: Date | moment.Moment) {
    this.setMyDate(value);
}

المشكلة عند تعيين القيم مباشرة لا تزال قائمة.

أي تحديث على هذا ، بعد أكثر من 5.5 سنوات؟

xhliu كما تشير التسميات ، إنه تقييد تصميم معقد للغاية بحيث لا يمكن معالجته ، وبالتالي تم إغلاق المشكلة. لا أتوقع أي تحديث. طول الفترة الزمنية لقضية مغلقة نوعًا ما غير ذي صلة.

يرجى إعادة الفتح. يجب أيضًا إضافة هذا على مستوى الواجهة. والمثال الكلاسيكي هو عند تنفيذ بروكسي حيث يكون لديك مرونة كاملة فيما يمكنك قراءته وكتابته.

xhliu ... معقد جدًا بحيث لا يمكن معالجته ...

https://ts-ast-viewer.com/#code/MYewdgzgLgBFCm0DyAjAVjAvDA3gKBkJgDMQQAuGAIhQEMAnKvAXzzwWXQDpSQg

const testObj = {
    foo: "bar"
}

testObj.foo

هذا الرمز foo يقول:

  foo
    flags: 4
    escapedName:"foo"
    declarations: [
      PropertyAssignment (foo)
    ]
    valueDeclaration: PropertyAssignment (foo)

يوجد متسع كبير هنا للحصول على معلومات إضافية ، وهو تصميم رائع نيابة عن TS. كل من صمم هذا ربما فعل ذلك على وجه التحديد لجعل الميزات الإضافية (حتى الكبيرة منها) ممكنة في المستقبل.

تخميني من هذه النقطة فصاعدًا:

إذا كان من الممكن التصريح (لمثال الرمز الزائف) عن accessDelclaration: AccessSpecification (foo) ، فإن PropertyAccessExpression الذي يعرف الرمز foo وإعلاناته ، يمكنه التحقق بشكل مشروط مما إذا كان هناك "إعلان وصول" و استخدم الكتابة من ذلك بدلاً من ذلك.

بافتراض وجود بناء الجملة لإضافة هذه الخاصية accessDelclaration إلى رمز "foo" ، يجب أن يكون PropertyAccessExpression قادرًا على سحب "AccessSpecification" من الرمز الذي يحصل عليه من ts.createIdentifier("foo") وإنتاج أنواع مختلفة.

ts.createPropertyAccess(
  ts.createIdentifier("testObj"),
  ts.createIdentifier("foo")
)

من الناحية التخمينية ، يبدو أن الجزء الأصعب من هذا التحدي من المحتمل أن يكون مقدار تساقط الدراجات حول بناء الجملة (أو ربما فلسفة الشركة؟) ، ولكن من منظور التنفيذ ، يجب أن تكون جميع الأدوات موجودة. سيتم إضافة شرط واحد إلى الوظيفة ts.createPropertyAccess() ، ويجب إضافة فئة الإقرار لتمثيل هذا الشرط وتأثيراته إلى رمز خاصية الكائن.

تمت كتابة الكثير من الأمثلة الجيدة عن سبب الحاجة الماسة إلى ذلك (خاصةً بالنسبة لـ DOM و Angular).

سأضيف فقط أنني تعرضت لهذا اليوم عند ترحيل كود JS القديم إلى TS حيث لم ينجح تعيين string إلى window.location وكان علي أن أقوم بحل بديل as any 😟

تقوم خاصية Window.location للقراءة فقط بإرجاع كائن الموقع بمعلومات حول الموقع الحالي للمستند.

على الرغم من أن Window.location هو كائن موقع للقراءة فقط ، إلا أنه يمكنك أيضًا تعيين DOMString إليه. هذا يعني أنه يمكنك العمل مع الموقع كما لو كان سلسلة في معظم الحالات: الموقع = ' http://www.example.com ' هو مرادف للموقع. href = ' http://www.example.com ' .
مصدر

ترحيل كود JS القديم إلى TS حيث لم ينجح تعيين string إلى window.location وكان علي أن أقوم بحل بديل as any

هذا مثال رائع.

TS يحتاج هذا. هذا جزء طبيعي جدًا من JavaScript.

أرى أن القضية الحالية قد أغلقت. ولكن كما أفهم ، لم تتحقق هذه الوظيفة؟

AGluk ، يجب إعادة فتح هذه المشكلة.

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

القضايا ذات الصلة

DanielRosenwasser picture DanielRosenwasser  ·  3تعليقات

bgrieder picture bgrieder  ·  3تعليقات

wmaurer picture wmaurer  ·  3تعليقات

jbondc picture jbondc  ·  3تعليقات

manekinekko picture manekinekko  ·  3تعليقات