Typescript: اقتراح: نوع غير قابل للإلغاء

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

قم بإدخال صيغتين جديدتين لإعلان النوع بناءً على JSDoc

var myString: !string = 'hello world'; //non-nullable
var myString1: ?string = 'hello world'; // nullable 
var myString2: string = 'hello world'; // nullable 
var myString3 = 'hello world'; // nullable

حسب النوع الافتراضي هي لاغية.

علمان جديدان للمترجم:

  • inferNonNullableType اجعل المترجم يستنتج نوع non-nullable:
var myString3 = 'hello world' // typeof myString is '!string', non-nullable
  • nonNullableTypeByDefault (أعتقد أنه قد يكون هناك اسم أفضل):
var myString: !string = 'hello world'; // non-nullable
var myString1: string = 'hello world'; // non-nullable 
var myString2: ?string = 'hello world'; // nullable 
var myString3 = 'hello world' // non-nullable
Committed Fixed Suggestion

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

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

ال 358 كومينتر

أقترح استخدام نوع آخر غير السلسلة كمثال لأنه بطبيعته لاغٍ. : ص
يمكنني أن أدرك أن الأنواع غير القابلة للإلغاء تمثل مشكلة نظرًا لأن المستخدم والمترجم لـ "!" يتوقع أن يكون النوع دائمًا غير فارغ ، وهو أمر لا يمكن تأكيده حقًا في JavaScript. قد يحدد المستخدم شيئًا مثل هذا:

function(myNonNull:!myClass):void {
  myNonNull.foo();
}

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

الآن بعد قولي هذا ، ربما يكون الحل هو أنه بالنسبة لطرق المواجهة العامة ، يمكن أن تؤكد تلقائيًا أنها ليست فارغة. ولكن بعد ذلك يمكن للمترجم أن يؤكد أيضًا أنه لا يمكنك امتلاك ممتلكات عامة (أو خاصة لهذه المسألة حقًا) يمكن أن يكون لها إعلان! nonnull نظرًا لأنه لا يمكن فرضها.

قد يتعمق هذا في مناقشة عقود التعليمات البرمجية ليتم إنفاذ ذلك بشكل صحيح.

سامح منتقدي ، أعتقد أن هناك حاجة قليلة جدًا لأنواع البيانات غير القابلة للإلغاء إذا / بمجرد توفر أنواع البيانات الجبرية. السبب الذي يجعل الناس يستخدمون null لتمثيل قيمة مفقودة هو أنه لا توجد طريقة أفضل للقيام بذلك في JavaScript وفي معظم لغات OOP على حد سواء. لذا فإن ADTs التصوير موجود هنا بالفعل. ثم ، بالنسبة للأحرف القديمة المكتوبة قبل non-nullables ، فإن وجودها لن يجعل الحياة أفضل. بالنسبة إلى libs الجديدة ، مع وجود ADT في مكانه ، يمكن للمرء أن يصمم بدقة ما يمكن أن تأخذه القيمة وفقًا لمواصفات مجال الأعمال دون استخدام القيم الخالية على الإطلاق. لذا أعتقد أن ما أقوله هو أن ADT هي وسيلة أكثر قوة لمعالجة نفس المشكلة.

أنا شخصياً كتبت للتو واجهة Maybe<T> واستخدمت الانضباط للتأكد من أن المتغيرات من هذا النوع ليست فارغة أبدًا.

أقترح استخدام نوع آخر غير السلسلة كمثال لأنه بطبيعته لاغٍ. : ص
يمكنني أن أدرك أن الأنواع غير القابلة للإلغاء تمثل مشكلة نظرًا لأن المستخدم والمترجم لـ "!" يتوقع أن يكون النوع دائمًا غير فارغ ، وهو أمر لا يمكن تأكيده حقًا في JavaScript. قد يحدد المستخدم شيئًا مثل هذا:

الوظيفة (myNonNull:! myClass): باطل {
myNonNull.foo () ،
}
ونظرًا لتعريفه بأنه غير فارغ ، فقد يكون كل شيء سعيدًا للمترجم ، ولكن بعد ذلك يقوم شخص آخر يستخدمه في جافا سكريبت بتمرير شيء فارغ وكابوم.

لا أفهم حقًا أنه يمكنك أيضًا تحديد:

function myFunc(str: string): int {
 return str && str.length;
}

وإذا قام شخص ما بتمرير int لهذه الوظيفة ، فسوف ينتهي به الأمر بخطأ أيضًا ، تتمثل ميزة الكتابة المطبوعة على التفويض إلى المترجم بتمرير الأشياء التي يمكنك التحقق منها يدويًا في جافا سكريبت ، مع إجراء فحص آخر لـ nullable / non يبدو النوع -nullable معقول بالنسبة لي. بالمناسبة ، يقوم SaferTypeScript و ClosureCompiler بالفعل بهذا النوع من الفحص.

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

var myNullableString: (null | string);
var myString = "hello";
myNullableString = myString //valid
myString = myNullableString // error null is not assignable to string;

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

var myString: string; // error
var myNullableString: (null | string); // no error

function myString(param1: string, param2?: string) {
  // param1 is string
  // param2 is (null | string)
}

fdecampredon +1

IIRC مما أظهره Facebook من Flow والذي يستخدم بناء جملة TypeScript ولكن مع أنواع غير قابلة للإلغاء بشكل افتراضي ، فإنها تدعم اختصارًا لـ (null | T) كما في منشورك الأصلي - أعتقد أنه كان ?T أو T? .

var myString: string; // error

قد يكون ذلك مزعجًا جدًا في الحالة التي تريد فيها تهيئة متغير بشروط ، على سبيل المثال:

var myString: string;
if (x) {
myString = a;
} else if (y) {
myString = b;
} else {
myString = c;
}

في Rust على سبيل المثال ، هذا جيد طالما أن المترجم يمكنه رؤية أن myString سيتم تهيئته قبل استخدامه ولكن استدلال TypeScript لا يدعم هذا في الوقت الحالي.

بصراحة ، القيام بشيء مثل var myString = '' بدلاً من var myString: string لا يزعجني كثيرًا ، لكن تأكد من أن هذا النوع من القواعد ممكن دائمًا.

fdecampredon +1 لهذا - تعجبني الفكرة كثيرًا. بالنسبة لقواعد التعليمات البرمجية التي تكون 100٪ JavaScript ، سيكون هذا قيدًا مفيدًا في وقت الترجمة فقط. (بما أنني أفهم اقتراحك ، فليس هناك نية لإنشاء كود لفرض ذلك؟)

بالنسبة لاختزال (null | string) بالتأكيد ?string جيد.
ومن المؤكد أن @ johnnyreilly هو مجرد فحص لوقت الترجمة

تجعل أنواع المجموع الأنواع غير القابلة للإلغاء افتراضيًا احتمالًا مثيرًا للاهتمام. لا يمكن المبالغة في خصائص السلامة الخاصة بـ non-nullable بشكل افتراضي. أنواع المجموع بالإضافة إلى "if / typeof التدمير" المخطط لها (لست متأكدًا مما يجب أن يسمى) تجعله آمنًا لدمج واجهات برمجة التطبيقات القابلة للصفر وغير القابلة للإلغاء.

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

من الجيد أن يتم جمع إجماع كبير على هذه التعريفات في DefinitelyTyped repo ، ولكن لا يزال لدي مخاوف عملية بشأن هذه الميزة.

samwgoldman الفكرة هي أن يكون لديك أنواع non-nullable فقط تحت علم مترجم خاص مثل nonImplicitAny يمكن تسمية هذه العلامة strict أو nonNullableType . لذلك لن يكون هناك تغييرات جذرية.

fdecampredon ماذا عن تعريفات الأنواع للمكتبات التي ليست من نوع TypeScript ، مثل تلك الموجودة في DefinitelyTyped؟ لم يتم التحقق من هذه التعريفات من قبل المترجم ، لذا فإن أي كود تابع لجهة خارجية يمكن أن يعرض قيمة خالية يجب أن يعاد شرحه لكي يعمل بشكل صحيح.

يمكنني أن أتخيل تعريفًا للنوع لوظيفة تم التعليق عليها حاليًا على أنها "سلسلة إرجاع" ، ولكنها أحيانًا تُرجع قيمة خالية. إذا كنت أعتمد على هذه الوظيفة في الكود البرمجي nonNullableType 'الخاص بي ، فلن يشكو المترجم (كيف يمكنه ذلك؟) ولم يعد الرمز الخاص بي آمنًا.

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

جانبا ، لا يوجد الكثير من المعلومات المتاحة حتى الآن على مترجم Flow على Facebook ، ولكن من تسجيل الفيديو للعرض التقديمي ، يبدو أنهم ذهبوا مع non-nullable افتراضيًا. إذا كان الأمر كذلك ، فهناك على الأقل بعض الأسبقية هنا.

حسنًا ، لنفترض أن هناك اختصارًا ? type لـ type | null | undefined .

fdecampredon ماذا عن تعريفات الأنواع للمكتبات التي ليست من نوع TypeScript ، مثل تلك الموجودة في DefinitelyTyped؟ لم يتم التحقق من هذه التعريفات من قبل المترجم ، لذا فإن أي كود تابع لجهة خارجية يمكن أن يعرض قيمة خالية يجب أن يعاد شرحه لكي يعمل بشكل صحيح.

يمكنني أن أتخيل تعريفًا للنوع لوظيفة تم التعليق عليها حاليًا على أنها "سلسلة إرجاع" ، ولكنها أحيانًا تُرجع قيمة خالية. إذا كنت أعتمد على هذه الوظيفة في الكود nonNullableType الخاص بي ، فلن يشكو المترجم (كيف يمكنه ذلك؟) ولم يعد الكود الخاص بي آمنًا.

لا أرى المشكلة ، تأكد من أن بعض ملفات التعريف لن تكون صالحة مع الوضع nonNullableType ، ولكن في معظم الأوقات تتجنب المكتبة الجيدة إرجاع null أو undefined لذلك سيظل التعريف صحيحًا مع غالبية الحالات.
على أي حال أنا شخصياً نادراً ما يمكنني اختيار تعريف من نوع DefinitelyTyped دون الحاجة إلى التحقق منه / تعديله ، سيكون لديك القليل من العمل الإضافي لإضافة بادئة ? مع بعض التعريفات.

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

لا أفهم سبب عدم وجود ميزة تبديل العلم ، فالقواعد ستكون بسيطة:

  • في الوضع العادي ? string ما يعادل string و null أو undefined قابل للتخصيص لجميع الأنواع
  • في الوضع nonNullableType ? string ما يعادل string | null | undefined و null أو undefined غير قابلة للتخصيص لأي نوع آخر غير null أو undefined

أين هو عدم التوافق مع ميزة تبديل العلم؟

تعتبر الأعلام التي تغير دلالات اللغة أمرًا خطيرًا. تتمثل إحدى المشكلات في أن التأثيرات يحتمل أن تكون غير محلية للغاية:

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

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

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

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

بالإضافة إلى تعليق RyanCavanaugh -> مما قرأته في مكان ما ، تذكر مواصفات / اقتراح ES7 استخدام التحميل الزائد للوظيفة (اسم الوظيفة نفسه ولكن نوع بيانات معلمة الإدخال المختلفة). هذه ميزة مطلوبة بشدة لجافا سكريبت.

من مستندات التدفق :

يعتبر التدفق قيمة مميزة ليست جزءًا من أي نوع آخر

var o = null;
print(o.x); // Error: Property cannot be accessed on possibly null value

يمكن جعل أي نوع T لتضمين فارغة (والقيمة ذات الصلة غير محددة) عن طريق الكتابة؟

var o: ?string = null;
print(o.length); // Error: Property cannot be accessed on possibly null or undefined value

[Flow] يفهم تأثيرات بعض اختبارات النوع الديناميكي

(أي في TS lingo يفهم حراس الكتابة)

var o: ?string = null;
if (o == null) {
  o = 'hello';
}
print(o.length); // Okay, because of the null check

محددات

  • عمليات التحقق على خصائص الكائن محدودة بسبب إمكانية التعرج:

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

  • يمكن أن تكون عمليات التحقق من نوع guard-style زائدة عن الحاجة لخصائص الكائن.

[D] لا تتوقع أن يتم التعرف على حقل nullable على أنه غير فارغ في بعض الطرق لأنه يتم إجراء فحص فارغ في طريقة أخرى في التعليمات البرمجية الخاصة بك ، حتى عندما يكون واضحًا لك أن الفحص الفارغ كافٍ للسلامة في وقت التشغيل (لنقل ، لأنك تعلم أن استدعاءات الطريقة السابقة تتبع دائمًا استدعاءات الطريقة الأخيرة).

  • undefined غير محدد.

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

ماذا لو تمت إضافة الخيار في نفس الوقت عند تقديم النوع null (واختصار علامة الاستفهام)؟ قد يؤدي وجود نوع null في ملف إلى إجبار المترجم على وضع non-nullable لهذا الملف حتى لو لم تكن العلامة موجودة في سطر الأوامر. أم أن هذا أمر سحري للغاية؟

jbondc يبدو جيدًا. لكن المشكلة في ذلك هي أنه سينتهي بـ ! كل مكان: p

It's tempting to want to change JavaScript but the reality is a 'string' is nullable or can be undefined.

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

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

توجيهات مثل "use strict" التي تسبب تغييرات محددة في الدلالات هي بالفعل جزء من اللغة ؛ أعتقد أنه سيكون من المعقول أن يكون لديك توجيه "use nonnullable types" في TypeScript.

metaweta @ لا أعتقد أنه كافٍ ، على سبيل المثال ماذا يحدث إذا استهلكت وحدة غير فارغة وحدة لاغية:

//module A
export function getData(): string[] {
  return null;
}
//module B
'use nonnull'
import A = require('./A');

var data: string[] = A.getData();

data في الوحدة "ب" في الحقيقة لاغية ، ولكن بما أن 'use nonnull' لم يتم استخدامه في الوحدة "أ" هل يجب علينا الإبلاغ عن خطأ؟
لا أرى طريقة لحل هذه المشكلة باستخدام الميزة القائمة على التوجيه.

نعم،

var data: string[] = A.getData();

من شأنه أن يسبب خطأ. بدلاً من ذلك ، يجب عليك تقديم قيمة افتراضية عندما يُرجع null getData() null :

var data: string[] = A.getData() || [];

metaweta طيب ولكن كيف تعرف أنه خطأ؟ :)
نوع getData لا يزال '() => سلسلة []' هل ستتعامل تلقائيًا مع كل ما يأتي من 'وحدة قابلة للقيمة' على أنه 'nullable'؟

نعم ، تمامًا (ما لم يتم وضع علامة صراحة على نوع من الوحدة القابلة للإلغاء).

يبدو أنك تريد الآن علامة لكل ملف تحدد ما إذا كان الملف الافتراضي لاغيًا أم لا.

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

هل تبدأ المشاريع بعلامة المجمع هذه في وضع التشغيل أو الإيقاف افتراضيًا؟ إذا كان شخص ما يعمل على مشروع غير قابل للإلغاء الافتراضي وقام بإنشاء / التبديل إلى مشروع افتراضي ، فهل سيؤدي ذلك إلى حدوث ارتباك؟
أنا أعمل حاليًا مع "No Implicit Any" في معظم مشروعاتي ، وكلما صادفت مشروعًا لا يوجد به هذا الخيار ، فإنه يفاجئني.

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

كان RyanCavanaugh قلقًا بشأن غير المحلية ، ويتم تحديد نطاق التوجيهات معجمًا. لا يمكنك الحصول على المزيد من المواقع المحلية ما لم تكتب تعليقًا توضيحيًا لكل موقع. أنا لا أؤيد التوجيه بشكل خاص ؛ كنت أشير للتو إلى أن الخيار موجود وأنه معقول على الأقل مثل "استخدام صارم" في ES5. أنا شخصياً أؤيد الأنواع غير القابلة للإلغاء بشكل افتراضي ، لكن من الناحية العملية ، فات الأوان لذلك. بالنظر إلى هذه القيود ، أنا أؤيد استخدام! بطريقة ما. يتيح لك اقتراح jbondc التمييز بين

أنا آسف إذا لم أكن واضحًا ، كنت أتفق مع ريان وأضيف مخاوفي.

بصراحة ، إذا كان إضافة use not-null هو ثمن تجنب كل استثناءات المؤشر الفارغة ، فسأدفعها دون أي مشكلة ، مع الأخذ في الاعتبار أن null أو undefined يمكن تخصيصه لأي نوع هو الخطأ الأسوأ هذا المطبوع عليه في رأيي.

jbondc لم أستخدم "استخدام صارم" ولذلك

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

لذا فإن الكود:

function myfoo (mynumber: number) {
    return !!mynumber;
} 

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

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

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

jbondc الحق. Griffork : آسف لم أفهم سوء التفاهم هذا ؛ التوجيه هو تعبير سلسلة حرفية يظهر على أنه السطر الأول من إنتاج برنامج أو وظيفة إنتاج ويتم تحديد تأثيرها على هذا الإنتاج.

"use not-null";
// All types in this program production (essentially a single file) are not null

مقابل

function f(n: number) {
  "use not-null";
  // n is not null and local variables are not null
  function g(s: string) {
    // s is not null because g is defined in the scope of f
    return s.length;
  }
  return n.toFixed(2);
}

function h(n: number) {
  // n may be null
  if (n) { return n.toFixed(3); }
  else { return null; }
}

أنواع non-nullable عديمة الفائدة. أنواع non-nullable هي مستخدمات. هم عديم الفائدة. بدون فائدة! أنت لا تدرك ذلك ، لكنك لست بحاجة إليهم حقًا. ليس هناك معنى كبير في تقييد نفسك بالإعلان أنه من الآن فصاعدًا لن نستخدم القيم الخالية. كيف يمكنك تمثيل قيمة مفقودة ، على سبيل المثال في موقف تحاول فيه العثور على سلسلة فرعية غير موجودة؟ عدم القدرة على التعبير عن قيمة مفقودة (ما يفعله NULL الآن) لن يحل مشكلتك. سوف تتداول في عالم قاسٍ مع NULL في كل مكان بعالم قاسي بنفس القدر مع عدم وجود قيم مفقودة على الإطلاق. ما تحتاجه حقًا يسمى أنواع البيانات الجبرية التي (من بين العديد من الأشياء الرائعة الأخرى) تتميز بالقدرة على تمثيل قيمة مفقودة (ما تبحث عنه في المقام الأول وما يمثله NULL في العالم الحتمي). أنا بقوة ضد مضيفا غير nullables للغة، لأنه يبدو مثل نحوي غير مجدية / القمامة الدلالي الذي هو الحل السذاجة ومحرجا لمشكلة معروفة. يرجى القراءة عن الاختيارات في F # و ربما في Haskell بالإضافة إلى المتغيرات (المعروفة أيضًا باسم النقابات ذات العلامات ، والنقابات المميزة) ومطابقة الأنماط.

@ aleksey-bykov يبدو أنك غير مدرك أن JavaScript يحتوي على قيمتين فارغتين ، undefined و null . يتم إرجاع قيمة null في JavaScript فقط على regexp غير مطابق وعند إجراء تسلسل لتاريخ بتنسيق JSON. السبب الوحيد لوجودها في اللغة على الإطلاق هو التفاعل مع تطبيقات Java الصغيرة. المتغيرات التي تم التصريح عنها ولكن لم تتم تهيئتها هي undefined ، وليس null . الخصائص المفقودة في أحد العناصر ترجع undefined ، وليس null . إذا كنت تريد صراحة أن تكون undefined صالحة ، فيمكنك اختبار propName in obj . إذا كنت تريد التحقق مما إذا كانت الخاصية موجودة في الكائن نفسه وليس ما إذا كانت موروثة ، فاستخدم obj.hasOwnProperty(propName) . ترجع السلاسل الفرعية المفقودة -1: 'abc'.indexOf('d') === -1 .

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

إذا اعتمدنا أمرًا "use not-null" ، أود أيضًا "استخدام not-void" (ليس باطلًا أو غير محدد).

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

@ aleksey-bykov إذا كنت أكتب مكتبة وأريد أن أضمن أن المدخلات ليست فارغة ، يجب أن أقوم باختبارات وقت التشغيل لها في كل مكان. أريد إجراء اختبارات وقت الترجمة لها ، وليس من الصعب توفيرها ، ويوفر كل من Closure و Flow دعمًا للأنواع غير الفارغة / غير المحددة.

metaweta ، لا يمكنك ضمان نفسك من pleaseNonNullablesNumbersOnly(<any> null) . بعد تجميعها إلى js ، لا توجد قواعد على الإطلاق. ثانيًا ، لماذا تهتم؟ قلها بصوت عالٍ وواضح مقدمًا nulls are not supported, you put a null you will get a crash ، مثل إخلاء المسؤولية ، لا يمكنك ضمان نفسك من جميع أنواع الأشخاص هناك ، ولكن يمكنك تحديد نطاق مسؤولياتك. ثالثًا ، لا يمكنني التفكير في عنصر تحرير رئيسي رئيسي يكون مضادًا للرصاص لأي مستخدم قد يضعه كمدخلات ، ومع ذلك لا يزال يتمتع بشعبية جنونية. فهل جهدك يستحق العناء؟

@ aleksey-bykov إذا تم فحص عملاء مكتبتي أيضًا ، فبالتأكيد يمكنني أن أضمن أنني لن أحصل على قيمة خالية. هذا هو بيت القصيد من TypeScript. من خلال تفكيرك ، ليست هناك حاجة لأنواع على الإطلاق: فقط "قل بصوت عالٍ وواضح" في وثائقك ما هو النوع المتوقع.

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

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

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

metaweta ، من خلال منطقتي أنه يجب على عملائك عدم استخدام --noNullLiteral أجل الخيال. كل شيء آخر يبقى على حاله ، نفس الكود ، لا تقلق ، حل أخف مع الحد الأدنى من البصمة. بالعودة إلى وجهة نظري ، لنفترض أن الأنواع غير القابلة للإلغاء وجدت طريقها إلى TS ومتوفرة في نكهتين مختلفتين:

  • يمكن للمرء استخدام بناء الجملة ! للإشارة إلى نوع لا يمكن أن يأخذ قيمة خالية ، على سبيل المثال string! لا يمكن أن يأخذ قيمة خالية
  • المفتاح noNullsAllowed قيد التشغيل

ثم تحصل على جزء من json من الخادم الخاص بك فوق ajax مع القيم الخالية في كل مكان ، معنويًا: لا يمكن إصلاح الطبيعة الديناميكية لجافا سكريبت من خلال تعليق توضيحي من النوع فوقه

@ aleksey-bykov بنفس الرمز ، إذا كنت أتوقع كائنًا بخاصية رقمية x وحصلت على {"x":"foo"} من الخادم ، فلن يتمكن نظام النوع من منعه . هذا بالضرورة خطأ في وقت التشغيل ومشكلة لا مفر منها عند استخدام شيء آخر غير TypeScript على الخادم.

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

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

نعم نحن بحاجة ماسة لهذا. انظر إلى خطأ المليار دولار من قبل هواري. استثناء المؤشر الفارغ (NPE) هو الخطأ الأكثر شيوعًا في لغات البرمجة المكتوبة التي لا تميز القيمة الفارغة من الأنواع غير القابلة للإلغاء. من الشائع جدًا أن Java 8 أضاف Optional في محاولة يائسة لمحاربته.

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

الآن بعد أن كانت هناك نقابات وحراس كتابة في TS ، فإن نظام الكتابة قوي بما يكفي للقيام بذلك. السؤال هو ما إذا كان يمكن القيام بذلك بطريقة متوافقة مع الإصدارات السابقة. أنا شخصياً أشعر أن هذه الميزة مهمة بما يكفي لأن يكون TypeScript 2.0 غير متوافق مع الإصدارات السابقة في هذا الصدد.

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

هذا ليس عن _not use_ nulls. يتعلق الأمر بنمذجة جميع الأنواع المعنية بشكل صحيح. إنفاكت ، ستسمح هذه الميزة باستخدام القيم الخالية بطريقة آمنة - لن يكون هناك سبب لتجنبها بعد الآن! ستكون النتيجة النهائية مشابهة جدًا لمطابقة النمط على نوع جبري Maybe (باستثناء أنه سيتم إجراؤه باستخدام علامة if بدلاً من تعبير حالة)

وهذا لا يتعلق فقط بالقيم الفارغة. null و undefined متماثلان من الناحية الهيكلية (لا توجد وظائف / عوامل تشغيل تعمل على أحدهما ولكن ليس الآخر) لذلك يمكن تصميمهما بشكل جيد بما فيه الكفاية باستخدام نوع فارغ واحد في TS.

metaweta ،

يتم إرجاع القيمة الخالية في JavaScript فقط في regexp غير مطابق وعند إجراء تسلسل لتاريخ بتنسيق JSON.

ليس صحيحا على الإطلاق.

  • ينتج عن التفاعل مع DOM قيمة فارغة:
  console.log(window.document.getElementById('nonExistentElement')); // null
  • كما أشار @ aleksey-bykov أعلاه ، يمكن أن تعود عمليات ajax فارغة. في الواقع ، undefined ليس قيمة JSON صالحة:
 JSON.parse(undefined); // error
 JSON.parse(null); // okay
 JSON.stringify({ "foo" : undefined}); // "{}"
 JSON.stringify({ "foo" : null}); // '{"foo":null}'

ملحوظة: يمكننا التظاهر بأنه تم إرجاع undefined عبر ajax ، لأن الوصول إلى خاصية غير موجودة سينتج عنه undefined - وهذا هو السبب في أن undefined غير متسلسل.

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

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

أنا أتفق مع @ aleksey-bykov على هذا. على الرغم من أنه سيكون من الرائع للغاية إذا كان بإمكاننا تنبيهنا من TypeScript في وقت التجميع حول الأخطاء التي قدمتها null وغير محددة ، إلا أنني أخشى أنها ستؤدي فقط إلى إحساس زائف بالثقة وينتهي بها الأمر إلى اصطياد التوافه بينما لا يتم اكتشاف المصادر الحقيقية لـ null.

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

هذه في الحقيقة وسيطة _ for_ الأنواع غير القابلة للإلغاء. إذا كان بإمكان التخزين الخاص بك إرجاع قيم null Foo ، فإن نوع الكائن الذي تم استرداده من هذا التخزين هو Nullable <Foo> وليس Foo. إذا كان لديك بعد ذلك دالة تُرجع يُقصد بها إرجاع Foo ، إذن عليك أن تتحمل المسؤولية عن طريق التعامل مع القيمة الفارغة (إما أن تقوم بطرحها لأنك تعرف أفضل أو تتحقق من القيمة الفارغة).

إذا لم يكن لديك أنواع غير قابلة للإلغاء ، فلن تفكر بالضرورة في التحقق من وجود قيمة خالية عند إرجاع الكائن المخزن.

أخشى أنه لن يؤدي إلا إلى إحساس زائف بالثقة وينتهي به الأمر إلى اصطياد التوافه بينما لا يتم اكتشاف المصادر الحقيقية للصفر.

ما نوع non-trivia الذي تعتقد أن الأنواع غير القابلة للإلغاء سوف تفوتها؟

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

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

أنا أتفق مع @ aleksey-bykov على هذا. على الرغم من أنه سيكون من الرائع للغاية إذا كان بإمكاننا تنبيهنا من TypeScript في وقت التجميع حول الأخطاء التي قدمتها null وغير محددة ، إلا أنني أخشى أنها ستؤدي فقط إلى إحساس زائف بالثقة وينتهي بها الأمر إلى اصطياد التوافه بينما لا يتم اكتشاف المصادر الحقيقية لـ null.

لن يكون استخدام أنواع nullable مطلبًا مطلقًا. إذا كنت تشعر أنه من غير الضروري وضع نموذج للحالات التي تُرجع فيها الطريقة قيمة خالية لأنها "غير مهمة" ، فلا يمكنك استخدام نوع nullable في تعريف النوع هذا (والحصول على نفس عدم الأمان كما هو الحال دائمًا). ولكن لا يوجد سبب للاعتقاد بأن هذا النهج سيفشل - هناك أمثلة على اللغات التي نفذتها بنجاح بالفعل (مثل Kotlin by JetBrains )

@ aleksey-bykov بصراحة ، لقد فهمت الأمر بشكل خاطئ تمامًا ، أحد أفضل الأشياء في الأنواع غير القابلة للإلغاء هو _إمكانية التعبير عن نوع على أنه nullable_.
مع استراتيجيتك المتمثلة في عدم استخدام القيمة الفارغة مطلقًا لمنع خطأ المؤشر الفارغ ، فإنك تفقد تمامًا إمكانية استخدام null خوفًا من حدوث خطأ ، وهذا سخيف تمامًا.

شيء آخر من فضلك في مناقشة حول ميزة اللغة لا تتماشى مع التعليقات الغبية مثل:

أنواع non-nullable عديمة الفائدة. أنواع non-nullable هي مستخدمات. هم عديم الفائدة. بدون فائدة! أنت لا تدرك ذلك ، لكنك لست بحاجة إليهم حقًا.

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

أنا لست ضد إدخال تعليق توضيحي من النوع غير الفارغ على الإطلاق. لقد ثبت أنه مفيد في C # ولغات أخرى.

لقد غيّر البروتوكول الاختياري مسار المناقشة بما يلي:

بصراحة ، إذا كانت إضافة use not-null هي ثمن تجنب كل استثناءات المؤشر الفارغة ، فسأدفعها دون أي مشكلة ، معتبرة أن القيمة الفارغة أو غير المحددة قابلة للتخصيص لأي نوع هي الخطأ الأسوأ الذي ارتكبته الكتابة المطبوعة في رأيي.

كنت أشير فقط إلى انتشار null و undefined في البرية.

يجب أن أضيف أيضًا أن أحد الأشياء التي أقدرها حقًا في TypeScript هو موقف عدم التدخل في اللغة. لقد كان نسمة من الهواء النقي.

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

لقد رأيت عددًا من الحجج تدور حول سبب احتياجنا / عدم حاجتنا إلى ذلك وأريد معرفة ما إذا كنت أفهم جميع الحالات الأساسية التي تمت مناقشتها حتى الآن:

1) تريد معرفة ما إذا كانت الوظيفة يمكنها إرجاع قيمة فارغة أم لا (بسبب نمط التنفيذ الخاص بها ، وليس بسبب الكتابة).
2) تريد معرفة ما إذا كانت القيمة يمكن أن تكون خالية.
3) تريد معرفة ما إذا كان كائن البيانات يمكن أن يحتوي على قيم خالية.

الآن ، هناك حالتان فقط يمكن أن يحدث فيهما الموقف 2: إذا كنت تستخدم القيم الخالية أو إذا كانت الدالة ترجع قيمة خالية. إذا قمت بإزالة جميع القيم الخالية من التعليمات البرمجية الخاصة بك (على افتراض أنك لا تريدها) ، فإن الموقف 2 يمكن أن يحدث فقط كنتيجة للموقف 1.

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

يتم حل الحل 2 أيضًا من خلال هذا.

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

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

(الإرسال من الهاتف ، آسف على الأخطاء)

fdecampredon ، هذا ليس الخوف في المقام الأول ، إنه استخدام القيمة null غير ضروري. أنا لا أحتاجهم. كمكافأة لطيفة ، حصلت على مشكلة استبعاد الاستثناءات المرجعية الفارغة. كيف كل هذا ممكن؟ من خلال استخدام نوع المجموع مع حالة فارغة. تعد أنواع المجموع ميزة أصلية لجميع لغات FP مثل F # و Scala و Haskell جنبًا إلى جنب مع أنواع المنتجات التي تسمى أنواع البيانات الجبرية. قد تكون الأمثلة القياسية لأنواع المجموع مع حالة فارغة اختيارية من F # وربما من Haskell. لا يحتوي TypeScript على ADT ، ولكن بدلاً من ذلك لديه مناقشة جارية حول إضافة non-nullables من شأنها أن تمثل حالة خاصة واحدة لما قد تغطيه ADT. لذا فإن رسالتي هي ، التخلص من العناصر غير الفارغة لـ ADT.

spion ، أخبار سيئة: F # حصلت على

نحن نستخدم null ولكن ليس بنفس الطريقة التي يستخدمها الناس. يأتي كود المصدر لشركتي في جزأين.

1) عندما يتعلق الأمر بالبيانات (مثل بيانات قاعدة البيانات) ، فإننا نستبدل البيانات الفارغة ببيانات فارغة في وقت إعلان المتغير.

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

البيانات التي لا نريد أن تكون لاغية لأنها بيانات العملاء وسوف يرون كلمات فارغة.

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

على الطرف الآخر ، من المستحيل تحديد نوع الاتحاد مع استبعاد القيمة الخالية ، ولهذا السبب نحتاج إلى نوع غير فارغ.

fdecampredon ، TS لا يحتوي على ADT ، ولديه نقابات ليست من أنواع الجمع ، لأنه ، كما قلت ، 1. لا يمكنهم نمذجة حالة فارغة بشكل صحيح نظرًا لعدم وجود نوع وحدة ، 2. لا توجد طريقة موثوقة للتدمير معهم

يمكن تنفيذ مطابقة النمط لـ ADT بطريقة تتماشى بشكل وثيق مع JavaScript الذي تم إنشاؤه ، على أي حال آمل ألا تكون هذه الحجة نقطة تحول

@ aleksey-bykov هذه ليست F #. إنها لغة تهدف إلى نمذجة أنواع JavaScript. تستخدم مكتبات JavaScript قيمًا خالية وغير محددة. لذلك ، يجب نمذجة هذه القيم بالأنواع المناسبة. نظرًا لعدم دعم ES6 لأنواع البيانات الجبرية ، فليس من المنطقي استخدام هذا الحل نظرًا لأهداف تصميم TypeScript

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

أنا مندهش من عدم ذكر أي شخص للتغييرات الضخمة التي قد يحتاجها lib.d.ts لإدخالها والمشكلات المحتملة للحالة الفارغة المؤقتة لحقول الفصل أثناء استدعاء المُنشئ. هذه بعض المشكلات الحقيقية والفعلية المحتملة لتنفيذ أنواع غير قابلة للإلغاء ...

Griffork الفكرة هي تجنب وجود عمليات تحقق فارغة في كل مكان في التعليمات البرمجية الخاصة بك. لنفترض أن لديك الوظيفة التالية

declare function getName(personId:number):string|null;

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

function doSomethingWithPersonsName(personId:number) {
  var name = getName(personId);
  if (name != null) return doThingsWith(name); // type guard narrows string|null to just string
  else { return handleNullCase(); }
}

والآن أنت رائع! يضمن نظام النوع أنه سيتم استدعاء doThingsWith باسم غير فارغ

function doThingsWith(name:string) {
  // Lets create some funny versions of the name
  return [uppercasedName(name), fullyLowercased(name), funnyCased(name)]
}

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

function justUppercased(personId:number) {
  var name = getName(personId);
  return uppercasedName(name); // error, passing nullable to a function that doesn't check for nulls.
}

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

وإذا كنت لا تحب التتبع لأنك تشعر أنه لا يستحق نمذجة إمكانية وجود قيمة فارغة ، فيمكنك العودة إلى السلوك القديم الجيد غير الآمن:

declare function getName(personId:number):string;

وفي هذه الحالات ، ستنبهك الكتابة المطبوعة فقط إذا فعلت شيئًا خاطئًا بشكل واضح

uppercasedName(null);

بصراحة لا أرى سلبيات ، باستثناء التوافق مع الإصدارات السابقة.

fdecampredon أنواع الاتحاد هي فقط تلك النقابات. هم ليسوا نقابات منفصلة ويعرف أيضًا باسم مبالغ. انظر # 186.

@ aleksey-bykov

لاحظ أن إضافة نوع الخيار لا يزال يمثل تغييرًا مفاجئًا.

// lib.d.ts
interface Document {
    getElementById(id: string): Maybe<Element>;
}

...

// Code that worked with 1.3
var myCanvas = <HTMLCanvasElement>document.getElementById("myCanvas");
// ... now throws the error that Maybe<Element> can't be cast to an <HTMLCanvasElement>

بعد كل شيء ، يمكنك الحصول على أنواع خيارات الرجل الفقير الآن مع التدمير

class Option<T> {
    hasValue: boolean;
    value: T;
}

var { hasValue, myCanvas: value } = <Option<HTMLCanvasElement>> $("myCanvas");
if (!hasValue) {
    throw new Error("Canvas not found");
}
// Use myCanvas here

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

لاحظ أنني أيضًا لا أؤيد الأنواع غير الفارغة افتراضيًا (ليس لـ TS 1.x على أي حال). إنه تغيير كبير للغاية.

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

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

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

@ aleksey-bykov

لم أقل أننا بحاجة إلى استبدال التواقيع ، كل التوقيعات الموجودة تبقى كما هي

_I_ قلت أنه يجب استبدال التواقيع ، وقد ذكرت السبب بالفعل:

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


لا شيء يتم جعله غير فارغ بشكل افتراضي. أبدا.

بالنسبة إلى TS 1.x ، أوافق ، فقط لأنه تغيير كبير للغاية. بالنسبة للإصدار 2.0 ، سيكون استخدام نوع الخيار في التوقيعات الافتراضية (lib.d.ts وما إلى ذلك) بمثابة تغيير فاصل بالفعل. يصبح جعل الأنواع غير قابلة للإلغاء افتراضيًا _ بالإضافة إلى ذلك_ أمرًا يستحق العناء ولا يحمل أي سلبيات.

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

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

كانت الوظيفة bar من مثالك تُعرف باسم nullable منذ بداية الزمن ، وقد تم استخدامها في حوالي 100500 تطبيق في كل مكان وعامل الجميع النتيجة على أنها لاغية ، والآن جئت ونظرت بداخلها واكتشفت أن القيمة null مستحيل ، هل يعني أنه يجب عليك المضي قدمًا وتغيير التوقيع من لاغٍ إلى غير قابل للإلغاء؟ أعتقد أنه لا يجب عليك ذلك. لأن هذه المعرفة ، على الرغم من قيمتها ، لا تستحق كبح 100500 تطبيق. ما يجب عليك فعله هو ابتكار تحرير جديد بتوقيعات منقحة تفعل ذلك على النحو التالي:

old_lib.d.ts

...
declare function bar(): number; // looks like can return a potentially nullable number
...

تمت المراجعة_الحافظة_د.تس

declare function bar(): !number; // now thank to the knowledge we are 100% certain it cannot return null

الآن تستمر التطبيقات القديمة في استخدام old_lib.d.ts ، بالنسبة للتطبيقات الجديدة ، يمكن للمطور اختيار revised_libs.d.ts

للأسف ، تحتوي updated_libs.d.ts على 50 وظيفة أخرى لم ألق نظرة عليها حتى الآن ، وكلها أرقام مرجعية (لكنني لا أعرف ما إذا كانت بالفعل رقمًا أم لا). ماذا الان؟

حسنًا ، خذ وقتك ، واطلب المساعدة ، واستخدم الإصدار (اعتمادًا على مستوى المعرفة التي اكتسبتها حتى الآن ، قد ترغب في إصدارها تدريجياً مع رقم إصدار متزايد باستمرار: revised_lib.v-0.1.12.d.ts )

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

أنا لست من محبي! فقط لأن كتابة المزيد من الأمتعة (من حيث ضغطات المفاتيح والحاجة إلى تذكر استخدامها). إذا كنا نريد non-nullability في 1.x ثم! هو أحد الخيارات التي تمت مناقشتها بالفعل أعلاه ، لكنني ما زلت أقول إن إجراء تغيير نهائي مع 2.0 وجعل عدم الإلغاء الافتراضي يستحق كل هذا العناء.

من ناحية أخرى ، ربما يؤدي ذلك إلى وضع Python 2/3-esque ، حيث لا يقوم أحد بالترقية إلى TS 2.0 لسنوات لأنهم لا يستطيعون تحمل تكلفة المرور عبر قاعدة الأكواد المكونة من مليون سطر للتأكد من أن كل إعلان متغير وفئة معلمة العضو والوظيفة و ... مشروحة بـ؟ إذا كان يمكن أن يكون فارغًا. حتى 2to3 (أداة ترحيل Python 2 إلى 3) لا يتعين عليها التعامل مع تغييرات واسعة النطاق من هذا القبيل.

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

ربما يجب أن نسأل مستخدمي Funscript كيف يقومون بالتوفيق بين واجهة برمجة تطبيقات DOM التي ترجع القيم الخالية مع F # (يستخدم Funscript ملف lib.d.ts الخاص بـ TypeScript و d.ts الأخرى للاستخدام من كود F #). لم أستخدمه مطلقًا ، ولكن بالنظر إلى http://funscript.info/samples/canvas/index.html على سبيل المثال ، يبدو أن موفر النوع لا يعتقد أن document.getElementsByTagName ("canvas") [0] يمكن أن يكون غير معرف.

تحرير: هنا يبدو أن document.getElementById () لا يتوقع أن يعود فارغًا. على الأقل لا يبدو أنه يعيد الخيار <العنصر> لأنه يصل إلى .onlick على النتيجة.

تضمين التغريدة
شكرا ، لم أفكر في ذلك.

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

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

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

في أي حال ، يحتوي Facebook's Flow على أنواع غير قابلة للإلغاء. بمجرد نضجها ، قد يكون من المفيد التحقيق كبديل لـ TS لمن يهتم منا بهذه المشكلة.

spion ، الرقم الوحيد الذي تعطينا قائمتك هو عدد المرات الخالية أو غير المحددة التي تم ذكرها لأي سبب من الأسباب ، في الأساس تقول فقط أنه تم الحديث عن null و undefined ، آمل أن يكون من الواضح أنه لا يمكنك استخدامه كحجة

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

حاولت تضييق نطاق التصفية باستخدام كلمات رئيسية مختلفة (أعتقد أنني) تمكنت من الحصول عليها جميعًا

تضمين التغريدة
سؤال: كم عدد هذه الأخطاء التي تحدث في المواقع التي قد يحتاجون إليها لتمييز المتغير على أنه لاغٍ أو غير قابل للتحديد بالرغم من ذلك؟

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

أسباب الاحتفاظ بـ null بسهولة الاستخدام كما هي الآن:
• إنها قيمة افتراضية أفضل من القيمة غير المحددة.
. 1) من الأسرع التحقق في بعض الحالات (يجب أن يكون رمزنا عالي الأداء)
. 2) يجعل من الحلقات في العمل أكثر توقعًا على الكائنات.
. 3) يجعل استخدام المصفوفة أكثر منطقية عند استخدام القيم الخالية للقيم الفارغة (على عكس القيم المفقودة). لاحظ أن delete array[i] و array[i] = undefined لهما سلوك مختلف عند استخدام indexOf (وربما طرق مصفوفة شائعة أخرى).

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

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

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

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

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

Griffork عندما أقول non-nullable ، أعني non-nullable-or-undefined. وليس صحيحًا أن هذا غير ممكن بسبب طريقة عمل JS. مع ميزات type guard الجديدة ، بمجرد استخدامك لـ type guard ، فأنت تعلم أن القيمة المتدفقة من هناك لا يمكن أن تكون فارغة أو غير محددة بعد الآن. هناك مقايضة متضمنة هناك أيضًا ، وأنا أقدم تطبيق Facebook Flow كدليل على أنه قابل للتنفيذ تمامًا.

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

تضمين التغريدة
أعتقد أنني أفهم أخيرًا من أين أتيت.

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

أستطيع أن أرى أن ذلك مفيد.

أعتقد أيضًا أن هذا لن يكون حقًا الحل الفضي الذي تأمل فيه.

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

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

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

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

الآن يعمل undefined and null بشكل مختلف ، لا يزال من الممكن أن تظهر الأخطاء غير المحددة في كل مكان ، ولا يمكن أن تضمن علامة الاستفهام أن القيمة ليست فارغة ، وتتصرف اللغة بشكل مختلف عن Javascript (وهو ما تحاول TS تجنبه مما رأيته ).

ملاحظة

foo(thing: whatiknowaboutmyobject) {
    if (thing.hidden) {
        delete thing.description;
    }
}

if (typeof thing.description === "string") {
    //thing.description is non-nullable now, right?
    foo(thing);
    //What is thing.description?
    console.log(thing.description.length);
}

TS بالفعل عرضة لتأثيرات التعرج (مثل أي لغة تسمح بقيم قابلة للتغيير). سيؤدي هذا إلى تجميع بدون أي أخطاء:

function foo(obj: { bar: string|number }) {
    obj.bar = 5;
}

var baz: { bar: string } = { bar: "5" };

foo(baz);

console.log(baz.bar.charAt(0)); // Runtime error - Number doesn't have a charAt method

تنص مستندات Flow على هذا فقط للاكتمال.

تحرير: مثال أفضل.

عمر او قديم:

نعم ، لكني أزعم أن وسيلة تحديد شيء ما إلى غير معرف أكبر بكثير من وسائل تحديد شيء ما على قيمة أخرى.

أعني،

mything.mystring = 5; // is clearly wrong.
delete mything.mystring; //is not clearly wrong - this is not quite the equivalent of setting mystring to >undefined.

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

Griffork للحصول على مثال حول كيفية قيام الكتابة المطبوعة الحالية بإيقاظك إلى شعور زائف بالأمان ، جرب المثال الذي قدمته في الملعب:

var mything = {mystring: "5"}; 
delete mything.mystring;
console.log(mything.mystring.charAt(1));

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

الادعاء بأن اللغة ستتصرف بشكل مختلف عن JavaScript صحيح ، لكنه لا معنى له. TypeScript لديها بالفعل سلوك مختلف عن JavaScript. كان الهدف من نظام الكتابة دائمًا هو عدم السماح بالبرامج التي لا معنى لها. يضيف نمذجة الأنواع غير القابلة للإلغاء ببساطة بضعة قيود إضافية. إن عدم السماح بتخصيص قيم خالية أو غير محددة لمتغير من نوع non-nullable هو بالضبط نفس عدم السماح بتعيين رقم لمتغير من نوع سلسلة. يسمح JS لكليهما ، ولا يسمح TS بأي منهما

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

الفكرة هي تجنب وجود عمليات تحقق فارغة في كل مكان في التعليمات البرمجية الخاصة بك

إذا فهمت ما تدافع عنه:

أ جعل كافة الأنواع غير خالية بشكل افتراضي.
ب- تعليم الحقول والمتغيرات التي تكون لاغية.
ج- تأكد من قيام مطور التطبيق / المكتبة بالتحقق من جميع نقاط الدخول إلى التطبيق.

لكن ألا يعني ذلك أن مسؤولية التأكد من خلو الكود من القيم الخالية يقع على عاتق الشخص الذي يكتب الكود وليس على المترجم؟ نحن نقول للمترجم بشكل فعال "dw ، أنا لا أترك أي قيم خالية في النظام."

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

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

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

jbondc أنا سعيد لأنك سألت ذلك. في الواقع تم وضع علامة CallExpression على أنها غير محددة أو لاغية. ومع ذلك ، لا يستفيد نظام النوع حاليًا من ذلك - فهو لا يزال يسمح بجميع العمليات على typeArguments كما لو لم يكن فارغًا أو غير محدد.

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

if (ce.typeArguments != null) {
  callSomethingOn(ce.typeArguments)
}

// callSomethingOn doesn't need to perform any checks

function callSomethingOn(na:NodeArray<TypeNode>) {
...
}

بمساعدة حراس النوع TS 1.4 ، داخل كتلة if ، سيتم تضييق نوع التعبير إلى NodeArray<TypeNode> والذي بدوره سيسمح لجميع عمليات NodeArray على هذا النوع ؛ بالإضافة إلى ذلك ، ستتمكن جميع الوظائف التي يتم استدعاؤها داخل هذا الشيك من تحديد نوع الوسيطة ليكون NodeArray<TypeNode> دون إجراء أي عمليات تحقق أخرى على الإطلاق.

ولكن إذا حاولت الكتابة

function someOtherFunction(ce: CallExpression) {
  callSomethingOn(ce.typeArguments)
}

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

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

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

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

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

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

التظاهر بأن حقلًا ما على كائن تم الحصول عليه عبر السلك (مثل ajax) غير باطل يعني الإيمان بالله

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

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

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

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

عندما تفكر قليلاً في وضع علامة على نوع على أنه غير فارغ في نظام نوع nullable له قيمة محدودة ، ستظل قادرًا على استهلاك نوع متغير / إرجاع مكتوب بدون أن تضطر إلى اختباره.
سيؤدي ذلك أيضًا إلى إجبار مؤلف التعريف على كتابة المزيد من التعليمات البرمجية ، نظرًا لأن المكتبة المصممة جيدًا في معظم الأوقات لا تُرجع أبدًا قيمة فارغة أو غير محددة.
أخيرًا ، حتى المفهوم غريب ، في نظام كتابة بنوع غير قابل للقيم ، يمكن التعبير عن النوع nullable تمامًا كنوع اتحاد: ?string يعادل string | null | undefined . في نظام الكتابة مع النوع nullable كإعداد افتراضي حيث يمكنك وضع علامة على النوع على أنه غير فارغ ، كيف يمكنك التعبير عن !string ؟ string - null - undefined ؟

في النهاية ، لا أفهم حقًا مخاوف الناس هنا ، null ليس سلسلة ، بالطريقة نفسها من 5 ليست سلسلة ، لن تتمكن كلتا القيمتين من يمكن استخدامها حيث يُتوقع وجود سلسلة ، ويكون السماح بالقسيمة var myString: string = null عرضة للخطأ مثل: var myString: string = 5 .
ربما يكون وجود قيمة فارغة أو غير محددة قابلة للتخصيص لأي نوع مفهومًا مألوفًا للمطور ، لكنه لا يزال مفهومًا سيئًا.

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

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

interface Foo {
        name: string;
        address: string|null; /* Nullable */
}

var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.toString(); // Error: do not use without null check

لكن ما أعترض عليه هو ما يلي:

foo.name = undefined; // Error: non-nullable

أشعر أن هذا سيتعارض مع الطريقة الطبيعية للعمل مع JavaScript.

يمكن أن ينطبق الشيء نفسه مع الرقم:

interface Foo {
        name: string;
        address: string|number; 
}
var foo:Foo = new FooClass();
foo.name.toString(); // Okay
foo.address.slice() // error

foo.name  = 5 // error

ولا يزال صالحًا في JavaScript

كم عدد الوقت الذي تقوم فيه عن طيب خاطر بتعيين قيمة خالية لخاصية كائن؟

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

تضمين التغريدة
كثير جدا في الواقع.

@ جريفورك ،

أعتقد أنه سيتم وضع علامة على معظم الأشياء على أنها لاغية

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

interface MyType {

     name: string;

     /** The date the entry was updated from Wikipedia or undefined for user-submitted content. */
     wikiDate: Date; /* Nullable */
}

غالبًا ما تُستخدم فكرة أن الحقل لاغٍ لتوفير المعلومات. وسيكتشف TypeScript الأخطاء إذا تطلب الأمر من النوع guard عند الوصول إلى wikiDate .

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

foo.name = 5 // error
ولا يزال صالحًا في JavaScript

صحيح ، لكن هذا خطأ لأن TypeScript يعرف بيقين 100٪ أنه لم يكن مقصودًا.

بينما

foo.name = غير محدد ؛ // لا ترسل الاسم إلى الخادم

متعمد تمامًا.

أعتقد أن التنفيذ الذي يلائم متطلباتنا بشكل وثيق هو عدم استخدام أنواع الاتحادات ، ولكن اتباع الاقتراح الأصلي:

 wikiDate: ?Date;

أتفق معNoelAbrahams

foo.name = 5 // خطأ
ولا يزال صالحًا في JavaScript
صحيح ، لكن هذا خطأ لأن TypeScript يعرف بيقين 100٪ أنه لم يكن مقصودًا.

يعلم المترجم فقط أنك قمت بتمييز الاسم كـ string وليس string | number إذا كنت تريد قيمة خالية ، فما عليك سوى وضع علامة ?string أو string | null (أي تعادل إلى حد كبير)

أعتقد أن التنفيذ الذي يلائم متطلباتنا بشكل وثيق هو عدم استخدام أنواع الاتحادات ، ولكن اتباع الاقتراح الأصلي:

تاريخ ويكي:؟ تاريخ ؛

لذلك نحن متفقون على أن النوع غير فارغ افتراضيًا ويمكنك وضع علامة nullable بـ ? :).
لاحظ أنه سيكون نوعًا اتحادًا نظرًا لأن ?Date يعادل Date | null | undefined :)

آسف ، كنت أحاول الموافقة على الإلغاء افتراضيًا وليس فارغًا بكتابة خاصة (الرموز مربكة).

fdecampredon ، ما يعنيه في الواقع هو أنه عندما يتم وضع علامة على حقل أو متغير على أنه

var wikiDate: ?Date;

wikiDate.toString(); // error
wikiDate && wikiDate.toString(); // okay

هذا ليس تغييرًا جذريًا ، لأنه لا يزال يتعين علينا القيام بذلك:

 var name: string;   // okay
 name.toString();  // if you think that's fine then by all means

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

يكون المثال الأول صحيحًا تمامًا عندما تفعل:

wikiDate && wikiDate.toString(); // okay

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

لكن المثال الثاني الخاص بك ليس جيدا

var name: string;   // okay
name.toString();  // if you think that's fine then by all means

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

var name: string;   // okay
name.toString();  // error because not initialized
var name: string;
if (something) {
  name = "Hello World";
} else {
  name = "Foo bar";
}
name.toString();  // no error since name will always be initialized.

لا أعرف بالضبط أين أضع الحاجز ، لكن من المؤكد أنه سيحتاج إلى نوع من الضبط الدقيق حتى لا يعيق المطور.

إنه تغيير فاصل ولا يمكن تقديمه قبل الإصدار 2.0 ، باستثناء ربما باستخدام التوجيه "use nonnull" الذي اقترحه metaweta

لماذا لا يكون لديك:

var string1: string; //this works like typescript does currently, doesn't need type-guarding before use, null and undefined can be assigned to it.
string1.length; //works
var string2: !string; //this doesn't work because the string must be assigned to a non-null and non-undefined value, doesn't need type-guarding before use.
var string3: ?string; //this must be type guarded to non-null, non-undefined before use.
var string4: !string|null = null; //This may be null, but should never be undefined (and must be type-guarded before use).
var string5: !string|undefined; //this may never be null, but can be undefined (and must be type-guarded before use).

ولديك علامة مترجم (تعمل فقط في حالة تشغيل -noimplicitany) والتي تقول -noinferrednulls ، مما يؤدي إلى تعطيل بناء الجملة العادي للأنواع (مثل string و int) وعليك توفير؟ أو ! معهم (لاغية وغير محددة وأي أنواع تمثل استثناءات).

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

أفكار؟

تحرير: لقد كتبت هذا لأنه يفرض فكرة استخدام non-null على أن تكون صريحة في كل فعل. أي شخص يقرأ الكود الذي يأتي من أي مشروع TS آخر سيعرف بالضبط ما يحدث. تصبح أيضًا علامة المترجم واضحة جدًا إذا كانت قيد التشغيل (حيث أن blah: string خطأ ، لكن blah:!string ليس كذلك ، على غرار الطريقة التي يعمل بها noimplicitany).

تحرير 2:
يمكن بالتأكيد ترقية نوع بالتأكيد لدعم noninferrednulls ، ولن يغيروا استخدام المكتبات إذا اختار الأشخاص عدم الاشتراك في؟ و ! خاصية.

لا يهمني ما إذا كانت non-null و non-undefined هي المشاركة ، أو الانسحاب ، أو مع
نوع المعدلات (!؟) ، أو توجيه ، أو علامة مترجم ؛ سأفعل مهما كان
يستغرق الحصول عليها - طالما كان من الممكن التعبير عنها - ، وهذا ليس كذلك
حاليا الحال.

في يوم الإثنين 22 ديسمبر 2014 الساعة 2:35 مساءً ، كتب Griffork [email protected] :

لماذا لا يكون لديك:

var string1: string ؛ // هذا يعمل مثل الكتابة المطبوعة حاليًا. ، لا تحتاج إلى نوع الحراسة قبل الاستخدام ، يمكن تعيين خالية وغير محددة لها.
طول ؛ // Worksvar string2:! string؛ // هذا لا يعمل لأنه يجب تعيين السلسلة إلى قيمة غير فارغة وغير محددة ، ولا تحتاج إلى حماية من النوع قبل use.var string3:؟ string؛ // يجب أن يكون هذا من النوع guarded to non-null، non-undefined قبل use.var string4:! string | null = null؛ // قد يكون هذا فارغًا ، لكن لا يجب أبدًا أن يكون غير معرف (ويجب أن يكون محميًا بالنوع قبل الاستخدام) .var string5:! string | undefined؛ // قد لا يكون هذا فارغًا أبدًا ، ولكن يمكن أن يكون غير معرف (ويجب أن يكون محميًا بالنوع قبل الاستخدام).

ولها علامة مترجم (هذا يعمل فقط إذا كان - noimplicitany قيد التشغيل)
يقول -noinferrednulls ، مما يؤدي إلى تعطيل بناء الجملة العادي للأنواع (مثل
السلسلة و int) وعليك توفير؟ أو ! معهم (null، undefined
وأي استثناءات).

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

أفكار؟

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -67899445
.

مايك ستاي- [email protected]
http://www.cs.a بجميع الأحوال .ac.nz/~mike
http://reperiendi.wordpress.com

Griffork هذا سيكون خيارًا ، لكنه سيء ​​في رأيي ، وسأشرح السبب:

  • سيؤدي ذلك إلى الكثير من الأعمال في ملفات التعريف ، حيث سيتعين علينا الآن التحقق من كل نوع والتعليق عليه للحصول على التعريف الصحيح.
  • سننتهي بكتابة !string (وأحيانًا ?string ) في كل مكان على الكود مما يجعل الكود أقل قابلية للقراءة.
  • !string في نظام يكون فيه النوع nullable هو مفهوم غريب والطريقة الوحيدة التي لا يمكنك وصفها بها حقًا هي string minus null minus undefined ، على العكس من ?string بسيط جدًا في الوصف نوع نظام حيث يكون النوع فارغًا افتراضيًا string | null | undefined .
  • أتوقع الكثير من الصداع (وفقدان الأداء) للعثور على خوارزمية فحص النوع حيث يفهم المترجم أن string | null يتطلب حارسًا من النوع ولكن string لا يتطلب ذلك ، فأنت تقدم مفهومًا حيث يجب معاملة بعض أنواع الاتحاد بشكل مختلف عن الآخر.
  • وأخيرًا ، الجزء الأسوأ الذي نفقده تمامًا استنتاج النوع var myString = "hello" ما يجب أن يكون myString string ، ?string أو !string ؟ بصراحة صداع كبير في المنظور هنا.

إذا لم يكن لدينا نوع فارغ كنوع افتراضي ، فإن أفضل اقتراح رأيته هنا هو التوجيه 'use non-null' اقترحهmetaweta.
من المؤكد أنه يجب تحديده بشكل جيد ولكن على الأقل باستخدام سلسلة use non-null في كل ملفاتنا ، يمكننا الحصول على سلوك بسيط يمكن التنبؤ به.

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

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

  4. إذا كنت تشير إلى ما كتبته على أنه !string|null ، فسيعمل ذلك في النظام الحالي إذا تم التعامل مع القيمة null _like_ {} (ولكن لم يكن قابلاً للتخصيص لها).
    إذا كنت تتحدث عن string|null الذي لم يكن موجودًا في قائمتي ، فأعتقد أنه يجب تجاهل قيمة null في هذه الحالة. لا معنى لها وغير محدد إلا في النقابات حيث يُسبَق كل غير باطل وغير محدد بعلامة! وأي منها ليس أيًا (قد يكون هذا تحذيرًا / خطأ للمترجم).
  5. سؤال جيد ، وهو سؤال لا يظهر إلا إذا كنت تستخدم الخيار - noimplicitnull ، أعتقد أن الخيار الأكثر أمانًا هو تعيينه لأي خيار من المرجح أن يتسبب في حدوث خطأ مبكر (ربما يكون لاغياً) ، لكنني أحصل على أشعر أن هناك فكرة أفضل لا أفكر فيها. أتساءل عما إذا كان لدى شخص آخر اقتراح حول كيفية التعامل مع هذا؟

تحرير: تمت الإضافة إلى النقطة 2.
تحرير: إصلاح خطأ مطبعي في النقطة 4.

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

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

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

لا أعتقد أن هناك مثل هذه الطريقة ولكن آمل أن أكون مخطئًا ، لأنها ذات قيمة لا تقدر بثمن في رأيي.
بالنسبة لجزء التغيير الفاصل ، لماذا أنت كذلك ضد التوجيه 'use non-null' ؟
لن يتأثر المطورون الذين يرغبون في نظام من النوع nullable على الإطلاق (ما لم يضيفوا بالفعل 'use non-null' في الجزء العلوي من ملفهم بغرابة ولكن بصراحة سيكون هذا غريبًا بعض الشيء)
والمطورين الذين يرغبون في نظام من النوع غير الفارغ يمكنهم فقط استخدام التوجيه الجديد.

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

إذا كنت تشير إلى ما كتبته كـ! string | null ، فسيعمل ذلك في النظام الحالي إذا تمت معاملة null مثل {} (ولكن لم يكن قابلاً للتخصيص له). إذا كنت تتحدث عن string | null الذي لم يكن موجودًا في قائمتي ، فأعتقد أنه يجب تجاهل قيمة null في هذه الحالة. لا معنى لها وغير محدد إلا في النقابات حيث يُسبَق كل غير باطل وغير محدد بعلامة! وأي منها ليس أيًا (قد يكون هذا تحذيرًا / خطأ للمترجم).
أنا أشير إلى حقيقة أنه عند تحليل الأنواع الثلاثة:

  • !sting : string - null - undefined
  • string : string | null | undefined
  • ?string : string | null | undefined

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

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

لن أفعل ذلك سيعرض بشكل أساسي نفس المشكلة التي علق عليها RyanCavanaugh عندما فكرت في تقديم علامة تسمح بالتبديل من القيمة الافتراضية إلى القيمة الفارغة كإعداد افتراضي.
في هذه الحالة كان الاقتراح السابق أبسط بكثير.

مرة أخرى لماذا أنت ضد التوجيه 'use non-null' ، أعتقد أكثر أنه يبدو لي الحل المثالي.

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

الفرق بين:
string
و
?string
هل هذا هو ؟ و ! يطلب الرمز التحقق من النوع الصارم لهذا المتغير (يشبه إلى حد كبير الأمر "use nonnull" ، ولكن على أساس كل متغير). اشرحها بهذه الطريقة وأعتقد أنك لن تواجه الكثير من المتاعب مع الأشخاص الذين يفهمونها.

الاختلاف بالطبع:
ملف 1:

//...187 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null;
    }
    else {
        return "hello";
    }
}

ملف 2:

"use nonnull"
//...2,748 lines of code down...
string myfoo(checker: boolean) {
    if(checker){
        return null; //Error!
    }
    else {
        return "hello";
    }
}

يتعين على المطورين الآن الاحتفاظ بخريطة ذهنية للملفات غير الخاملة وأي الملفات ليست كذلك. أعتقد بصدق أن هذه فكرة سيئة (حتى لو كان معظم _your_ code "استخدام nonnull").

تحرير: أيضًا عندما تبدأ في كتابة دالة وتحصل على نافذة صغيرة تخبرك بتعريف الوظيفة ، كيف تعرف ما إذا كان string في هذا التعريف لاغٍ أم لا؟

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

  • مرة أخرى هذا هو الحال بالفعل مع 'use strict' .
  • على الأقل الفكرة بسيطة. وإدخال مفهوم بسيط في نظام الكتابة.
  • إذا قام أحد المطورين بنقل وظيفة في ملف آخر يبدو أنه يمثل حالة ممتازة بالنسبة لي ، وبما أن الخطأ الذي سيُطلب منه سيكون حول التحقق من القيمة الفارغة ، فسيكون قادرًا على فهم ما يحدث بسرعة ...

مرة أخرى ، إنها ليست مثالية ، لكني لا أرى بديلاً أفضل.

فقط لكي أكون واضحًا ، فإن مشاكلك الوحيدة مع ما اقترحته (بعد الحجج المضادة) التي يمكنني رؤيتها هي:

  • لا نعرف كيف سيكون سلوك var mystring = "string"
  • لا تريد كتابة الرموز في كل مكان.

ومخاوفي من التوجيه هي:

  • لن تكون القيم الخالية من القيم الخالية صريحة (ولكن يمكن أن تحدث في نفس المشروع مثل العناصر الفارغة). تحرير : صياغة ثابتة لجعلها أكثر منطقية.
  • سيكون من الصعب على المطورين تتبع ما إذا كان ما يكتبونه باطلًا أم لا.
  • قد تكون تعريفات الوظائف التي تراها عند استدعاء دالة ( تحرير : النافذة المنبثقة التي يوفرها Visual Studio عند بدء كتابة دالة) قابلة للإلغاء ولن تكون قادرًا على تحديدها.
  • بمجرد التفاف الوظيفة في طبقتين أو ثلاث طبقات ، لا تعرف ما إذا كان تعريفها لا يزال صحيحًا (حيث لا يمكنك استخدام استدلال الكتابة من خلال الملفات التي لا تحتوي على "استخدام nonnull").

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

تحرير: أوضح نقطة.
تحرير: ضع سلسلة بين علامتي الاقتباس

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

أريد أن تكون جميع القيم غير الخالية صريحة (لأنها يمكن أن تحدث في نفس المشروع مثل العناصر الفارغة).

أريد حقيقة أن string هو في الواقع string | null | undefined بدون إجبارك على التحقق من ذلك بشكل صريح.

سيكون من الصعب على المطورين تتبع ما إذا كان ما يكتبونه باطلًا أم لا.

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

قد تكون تعريفات الوظائف التي تراها عند استدعاء دالة لاغية ولن تتمكن من تحديدها.

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

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

أنا لا أفهم وجهة نظرك هنا.

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

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

ولا أستطيع أن أفهم لماذا التوجيه أفضل من إجبار المطورين على أن يكونوا صريحين بشأن نواياهم

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

من وجهة نظري هناك 3 حلول قابلة للتطبيق هنا:

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

بالمناسبة ، سيكون العلم يستحق ذلك تمامًا بالنسبة لي.

function fn(x: string): number;
function fn(x: number|null): string;

function foo() {
    return fn(null);
}

var x = foo(); // x: number or x: string?

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

  1. string|null|undefined واضحًا في اقتراحي باستخدام العلم ، ولهذا السبب تركته.
  2. ثم لماذا يكون لكل ملف؟ أقترح اقتراحي لأنه _ لا يكسر التوافق العكسي_ وهو أمر مهم بالنسبة لي ، وربما الكثير من المطورين الآخرين. ويمكن إجبارها على أن تكون شاملة للمشروع.
  3. أنا أستخدم الكثير من الملفات التي لا أقوم بإنشائها ؛ كيف لي أن أعرف أن هذا الملف المعين قد "استخدم nonnull"؟ إذا كان لدي 100 ملف تم إنشاؤه بواسطة أشخاص آخرين ، فلا بد لي من حفظ أي من هذه الملفات المائة غير فارغ؟ (أو ، _في كل مرة_ أستخدم متغيرًا / دالة من ملف آخر ، لا بد لي من فتح هذا الملف والتحقق منه؟)
  4. دعنا نرى ما إذا كان بإمكاني توضيح ذلك: مثال في نهاية المنشور.
  5. اين توقف أين تسميها سيئة؟ يجب ألا تغير سلسلة واحدة أو كلمة أساسية في بداية الملف الطريقة التي يتصرف بها الملف. تمت إضافة "استخدام صارم" إلى جافا سكريبت لأن

    1. لم تكن هناك مجموعة كبيرة من جافا سكريبت (على سبيل المثال ، مطبوعة) في ذلك الوقت يمكنها أن تفعل ما تريد.

    2. لقد كانت محاولة لتسريع معالجة جافا سكريبت (وهو في نظري السبب الوحيد لعذره).

      تم اعتماد "use strict" - ليس لأنه كان الشيء الصحيح الذي ينبغي عمله - ولكن لأنها كانت الطريقة الوحيدة التي يمكن للمتصفحات من خلالها تلبية متطلبات المطورين. أكره أن أرى نصًا مكتوبًا يضيف 1 (ثم 2 ، ثم 3 ثم 4) توجيهات أخرى - تغير بشكل أساسي طريقة عمل اللغة - كسلاسل معلنة في نطاق تعسفي ، وتؤثر على بعض النطاقات التعسفية الأخرى. إنه حقًا تصميم لغة سيء. سأكون سعيدًا إذا لم يكن مصطلح "use strict" موجودًا في Typescript ، وبدلاً من ذلك كانت عبارة عن علامة مجمِّع (وأرسلتها Typescript إلى كل ملف / نطاق يحتاجها).

  6. Javascript ليس "نظام نوع غير قابل للقيم" ، و Typescript ليس "نظام نوع non-nullable". لا يعني تقديم القدرة على التصريح عن الأنواع غير القابلة للإلغاء أن النظام بأكمله "غير قابل للإلغاء". ليس الهدف من كتابته.
File 1:
"use notnull"
export string foo() {
    return "mygeneratedstring";
}
File 2:
export string foo() {
    return file1.foo()
}
File 3:
"use notnull"
file2.foo(); //???

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


يبدو أننا نتجادل في دوائر. إذا لم أفهمك ، فأنا آسف ، لكن يبدو أنك تثير نفس المشكلات مرارًا وتكرارًا ، وأنا أرد عليها مرارًا وتكرارًا.

تضمين التغريدة
في هذا المثال ، لا تكون السلسلة فارغة ضمنية (أعتقد أنها تفترض أن العلامة --noImplicitNull قيد التشغيل)

string | null | undefined كان صريحًا في اقتراحي باستخدام العلم ، ولهذا السبب تركته

ما أعنيه هو أنه في النظام الفعلي ، var myString: string هو ضمنيًا var myString: (string | null | undefined); وأن المترجم لا يجبرك على استخدام Typeguard ما لم يكن كل نوع الاتحاد الآخر.

ثم لماذا يكون لكل ملف؟ أقترح اقتراحي لأنه لا يكسر التوافق مع الإصدارات السابقة وهو أمر مهم بالنسبة لي ، وربما الكثير من المطورين الآخرين. ويمكن إجبارها على أن تكون شاملة للمشروع.
التوجيه لا يكسر التوافق مع الإصدارات السابقة (ما لم تستخدم سلسلة حرفية "use not-null" في موضع من التوجيه لسبب غريب جدًا ، وهو ما أراهن أنه لا أحد يفعل ذلك) ، وفي مرحلة ما أيضًا ، سيتعين على TypeScript كسر التوافق مع الإصدارات السابقة ، لهذا السبب حدد semver الإصدار الرئيسي ، لكن هذه قصة أخرى.

أنا أستخدم الكثير من الملفات التي لا أقوم بإنشائها ؛ كيف لي أن أعرف أن هذا الملف المعين قد "استخدم nonnull"؟ إذا كان لدي 100 ملف تم إنشاؤه بواسطة أشخاص آخرين ، فلا بد لي من حفظ أي من هذه الملفات المائة غير فارغ؟ (أو ، في كل مرة أستخدم فيها متغيرًا / دالة من ملف آخر ، يجب أن أفتح هذا الملف وأتحقق منه؟)

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

اين توقف أين تسميها سيئة؟ يجب ألا تغير سلسلة واحدة أو كلمة أساسية في بداية الملف الطريقة التي يتصرف بها الملف. تمت إضافة "استخدام صارم" إلى جافا سكريبت لأن
لم تكن هناك مجموعة كبيرة من جافا سكريبت (على سبيل المثال ، مطبوعة) في ذلك الوقت يمكنها أن تفعل ما تريد.
لقد كانت محاولة لتسريع معالجة جافا سكريبت (وهو في نظري السبب الوحيد لعذره). تم اعتماد "use strict" ليس لأنه كان الشيء الصحيح الذي ينبغي عمله ولكن لأنه كان الطريقة الوحيدة التي يمكن للمتصفحات من خلالها تلبية متطلبات المطورين. أكره أن أرى نصًا مكتوبًا يضيف 1 (ثم 2 ، ثم 3 ثم 4) توجيهات أخرى تغير بشكل جذري الطريقة التي تعمل بها اللغة كسلاسل معلنة في نطاق تعسفي ، وتؤثر على بعض النطاقات التعسفية الأخرى. إنه حقًا تصميم لغة سيء. سأكون سعيدًا إذا لم يكن مصطلح "use strict" موجودًا في Typescript ، وبدلاً من ذلك كانت عبارة عن علامة مجمِّع (وأرسلتها Typescript إلى كل ملف / نطاق يحتاجها).

تم تقديم `` use strict '' لتغيير سلوك اللغة والحفاظ على التوافق الرجعي ، وهي نفس المشكلة التي نواجهها الآن ، وسوف تختفي تقريبًا مع وحدات es6 (وبالتالي ، قد يختفي use not null مع الإصدار الرئيسي التالي من الكتابة المطبوعة) .

Javascript ليس "نظام نوع غير قابل للقيم" ، و Typescript ليس "نظام نوع non-nullable". لا يعني تقديم القدرة على التصريح عن الأنواع غير القابلة للإلغاء أن النظام بأكمله "غير قابل للإلغاء". ليس الهدف من كتابته.

هذه الجمل ليس لها أي معنى بالنسبة لي ، لا تحتوي JavaScript على نظام كتابة ثابت ، لذلك كما قلت ، يمكن مقارنة حقيقة أنه يمكنك تعيين null لمتغير كان قبل string بسهولة إلى حقيقة أنه يمكنك إسناد 5 إلى متغير كان عبارة عن سلسلة من قبل.
الاختلاف الوحيد هو أن TypeScript ، وهو مدقق من النوع تم إنشاؤه لجافا سكريبت ، يعتبر برأي أن null قابل للتخصيص لكل شيء وليس 5 .

على سبيل المثال الخاص بك ، نعم ، نفقد بعض المعلومات بين الملفات ، إنها مقايضة مقبولة بالنسبة لي ولكن يمكنني فهم مخاوفك.

يبدو أننا نتجادل في دوائر. إذا لم أفهمك ، فأنا آسف ، لكن يبدو أنك تثير نفس المشكلات مرارًا وتكرارًا ، وأنا أرد عليها مرارًا وتكرارًا.

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

Griffork بالمناسبة أعتقد أن فكرة spion تعمل ، الفكرة هي أن أقول أنه بدون العلم string أن يكون number | null لاغياً ضمنيًا ، سيأخذ دائمًا السيادة عند التحميل الزائد الأول ، لذلك في كليهما الحالة foo(null) ستكون string .

RyanCavanaugh هل تعتقد أنه يمكن أن يعمل؟

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

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

وفقًا لـ Facebook : "في JavaScript ، يتحول null ضمنيًا إلى جميع الأنواع البدائية ؛ وهو أيضًا عنصر ساكن صالح لأي نوع كائن."
السبب الذي يجعلني أريد أن يكون غير قابل للإلغاء صريحًا (في كل إجراء يقوم به المطور) هو أنه باستخدام non-nullable ، فإن المطور يقر بأنه يختلف عن طريقة عمل جافا سكريبت ، وأن النوع غير قابل للإلغاء ليس مضمونًا (لأن هناك العديد من الأشياء التي يمكن أن تؤدي إلى حدوث خطأ).
الخاصية التي تكون غير قابلة للإلغاء هي خاصية لا يمكن حذفها

لقد كنت أستخدم Typescript لفترة طويلة. كان من الصعب بالنسبة لي إقناع رئيسي في البداية بالتغيير من جافا سكريبت إلى تنكسكريبتي ، وكان من الصعب جدًا بالنسبة لي إقناعه بالسماح لنا بالترقية كلما حدث تغيير مفاجئ (حتى لو لم يكن الأمر سهلاً). عندما يتم دعم ES: Harmony بواسطة المتصفحات ، فربما نرغب في استخدامه. إذا حدث تغيير كسر في Typescript بين الآن وحتى عندما يدعم Typescript ES: Harmony (خاصةً السائد مثل non-nullable) أشك في أنني سأفوز بهذه الحجة. ناهيك عن مقدار الوقت والمال الذي سأكلفه بالنسبة لي لإعادة تدريب جميع مطورينا (أنا "شخصنا من النوع" Typescript "، وظيفتي هي تدريب مطوري Typecript).
وبالتالي ، فأنا ضد إدخال تغييرات جذرية.
لا أمانع في أن أتمكن من استخدام العناصر غير الفارغة بنفسي ، إذا كانت هناك طريقة يمكنني من خلالها إدخالها في الكود المستقبلي دون كسر الكود القديم (هذا هو سبب اقتراحي). من الناحية المثالية ، سيتم تحويل جميع التعليمات البرمجية الخاصة بنا في النهاية ، ولكن سيكلفنا وقتًا ومالًا أقل بكثير في تدريب الأشخاص واستخدام مكتبات الطرف الثالث.

تعجبني فكرة استخدام الرموز (حسب اقتراحي) بدلاً من التوجيه لكل ملف ، لأن لديهم القدرة على الانتشار دون فقدان السياق.

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

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

شكرا على المناقشة التي كانت

بافتراض أنك تقصد التنوير (أعمال مثيرة للاهتمام أيضًا: D) ، فقد استمتعت أيضًا بالمناقشة :).
حظًا سعيدًا في الشوكة الخاصة بك ، وآمل أن نحصل على شخص TS هنا لرفض بعض اقتراحاتنا على الأقل (لذلك نحن نعرف ما لا يريدون بالتأكيد تنفيذه).

لذا لمزيد من التفصيل في الحل المقترح للمشكلة التي وصفتها RyanCavanaugh ، بعد إضافة النوع --noImplicitNull ، فإن مدقق الكتابة عند التشغيل بدون العلامة سيكون له سلوك معدل:

كلما احتوى نوع زائد على |null (مثل المثال الموضح):

function fn(x: string): number;
function fn(x: number|null): string;

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

function fn(x: string): number;
function fn(x: number|null): string;
// implicit signature added at the end, caused by the first overload
function fn(x: null): number;

ينتج عن هذه النتائج نفس التفسير مثل الإصدار الصارم --noImplicitNull : عند تمرير قيمة فارغة ، يكون أول حمل زائد مطابق هو number|null الصريح. بشكل فعال يجعل القيم الخالية الصريحة أقوى من القيم الضمنية.

(نعم ، أعلم أن عدد التعريفات الضمنية يتزايد أضعافًا مضاعفة مع عدد الوسائط التي تحتوي على زيادة في التحميل على |null - آمل أن يكون هناك تطبيق أسرع "يسمح" بالقيم الخالية في مسار ثانٍ إذا لم تكن هناك أنواع أخرى مباراة ، لكنني لا أعتقد أنها ستكون مشكلة في كلتا الحالتين لأنني أتوقع أن يكون هذا الموقف نادرًا)

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

فيما يلي بعض الأفكار الأخرى حول بقية السلوك:

سيسمح أيضًا بتعيين القيم الخالية أو ترك القيم غير مهيأة للقيم من أي نوع عندما لا يتم تمرير العلم.

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

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

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

لقد قرأت للتو _ all_ التعليقات أعلاه وهذا كثير. إنه لأمر مخيب للآمال أنه بعد العديد من التعليقات و 5 أشهر ، ما زلنا نناقش نفس النقاط: بناء الجملة والعلامات ...

أعتقد أن العديد من الأشخاص قد قدموا حججًا قوية حول قيمة التحقق من القيم الفارغة الثابتة بواسطة المترجم في قواعد التعليمات البرمجية الكبيرة. لن أقول أي شيء أكثر من ذلك (لكن +1).

حتى الآن كان الجدل الرئيسي حول بناء الجملة ومسألة التوافق مع العديد من رموز TS الحالية وتعريفات مكتبة TS. أستطيع أن أرى سبب تقديم نوع فارغ ويبدو أن بناء الجملة ?string هو الأنظف والأكثر طبيعية. لكن هذا يغير معنى string وبالتالي فهو تغيير فاصل _ ضخم_ لكل _الشفرة الحالية.

الحلول المقترحة أعلاه لهذا التغيير العاجل هي تقديم إشارات المترجم ( 'use strict'; كسابقة. ليس لأن TS ورثت أخطاء JS السابقة التي يجب أن نكررها.

لهذا السبب أعتقد أن التعليق التوضيحي "ليس فارغًا" ( string! ، !string ، ...) هو السبيل الوحيد للذهاب وسأحاول تقديم حجة جديدة في المناقشة: ربما لا باس به.

لا أحد يريد أن يكون لديه غرة ( ! ) في جميع أنحاء الكود. لكن ربما لا يتعين علينا ذلك. يستنتج TS الكثير عن الكتابة.

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

var x: string;  // Nullable
x.toString();  // Error: x can be undefined or null
x = "jods";
x.toUpperCase();  // Fine.
notNull(x);  // Fine

غالبًا ما أستخدم مُهيئًا بدلاً من النوع.

var x = "jods";  // x: string (nullable)
notNull(x);  // Fine
x = null;  // Fine
notNull(x);  // Error

2. وظيفة إرجاع القيم
في كثير من الأحيان لا أحدد قيمة الإرجاع. تمامًا كما يستنتج TS النوع ، يمكن أن يستنتج ما إذا كان لاغياً أم لا.

function() /* : string! */ {
  return "jods";
}

تحرير: فكرة سيئة بسبب الوراثة ومتغيرات الوظيفة ، انظر تعليق Griffork أدناه
حدث إذا قدمت نوع إرجاع ، فإن TS قادر تمامًا على إضافة التعليق التوضيحي "ليس فارغًا" بالنسبة لي.

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

function f() : string {  // f is nullable although this implementation never returns null.
  return "abc";
}

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

function (x: string!) { return x.toUpperCase(); } // OK
function (x: string) { return x.toUpperCase(); } // Error

لتجنب أخطاء التحويل البرمجي في قواعد التعليمات البرمجية القديمة ، نحتاج إلى علامة جديدة "Null dereference is a error" ، تمامًا كما لدينا "لا يوجد أي إشارة ضمنية". _ هذه العلامة لا تغير تحليل المترجم ، فقط ما تم الإبلاغ عنه كخطأ! _
هل هذا كثير من الانفجارات لإضافتها؟ يصعب القول دون إجراء إحصائيات على قواعد أكواد حقيقية وكبيرة. لا تنس أن المعلمات الاختيارية شائعة في JS وكلها لاغية ، لذلك ستحتاج إلى هذه التعليقات التوضيحية إذا كانت "not null" هي القيمة الافتراضية.
هذه الانفجارات هي أيضًا تذكيرات لطيفة بأن القيمة الفارغة غير مقبولة للمتصلين ، بما يتفق مع ما يعرفونه عن string اليوم.

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

class C {  x: string!; }

function(c: C!) // : string! is inferred
{ return c.x; } // OK, but annotations are required

يمكننا تخيل اختصار للمهيئ ، ربما:

class C {
  x! = "jods"; // Note the bang: x is inferred as !string rather than just string.
}

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

5. الإقرارات ، .d.ts
الفائدة الكبيرة هي أن جميع المكتبات الموجودة تعمل. يقبلون القيم الخالية وغير الفارغة كمدخلات ويفترض أنهم يعرضون قيمة فارغة محتملة.
في البداية ، ستحتاج إلى تحويل بعض قيم الإرجاع بشكل صريح إلى "not null" ، لكن الوضع سيتحسن حيث يتم تحديث التعريفات ببطء للإشارة إلى عدم إرجاعها مطلقًا. يعد التعليق التوضيحي للمعلمات أكثر أمانًا ولكن ليس مطلوبًا للترجمة بنجاح.

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

var a: { s: string } = something(); // Notice s is nullable.
notNull(a.s); // Error
notNull(<!>a.s);  // OK, this is a shorthand "not-null" cast, because the dev knows about something().

أعتقد أن القدرة على تلميح المترجم - بطريقة بسيطة - أن شيئًا ما ليس فارغًا أمر مرغوب فيه. هذا صحيح أيضًا لـ not-null افتراضيًا ، على الرغم من صعوبة الحصول على بناء جملة منطقي بسيط.

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

أتفق مع معظم ما قلته على @ jods4 ولكن لدي بعض الأسئلة والمخاوف:

2) إذا كان المترجم يمكنه تلقائيًا تحويل nullable إلى non nullable في أنواع الإرجاع ، فكيف يمكنك تجاوز هذا (على سبيل المثال للوراثة أو الوظائف التي يتم استبدالها)؟

3 & 5) إذا كان بإمكان المترجم تحويل القيمة nullable إلى non nullable ، فيمكن الآن لتوقيع الوظيفة في ملف التعليمات البرمجية أن يتصرف بشكل مختلف عن نفس توقيع الوظيفة في ملف d.ts.

6) كيف يعمل / مظهر الدوي عند استخدام أنواع الاتحاد؟

2) صيد جيد. لا أعتقد أنه سيعمل بشكل جيد مع الميراث (أو "المندوبين") حقًا.
حيث أعمل ، فإن تجربتنا مع TS هي أننا لا نكتب بشكل صريح قيم الإرجاع من الدوال. يقوم TS برسمها وأعتقد أن TS يمكن أن يكتشف الجزء الفارغ / غير الفارغ.

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

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

3 و 5) مع التغيير أعلاه ، لم يعد الأمر كذلك ، أليس كذلك؟ يعمل توقيع الوظائف في الكود والتعريفات الآن بنفس الطريقة تمامًا.

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

var x: number | string!; // compiler error
var x: number | string; // x can be null
var x: number! | string!; // x cannot be null
function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable
function f<T, G>(): T | G; // f is nullable if T or G is nullable
function f<T>(): T | null; // f is nullable even if T is not nullable.
function f<T>(): T!; // Whatever T is, f never returns null.

// Generics constraint option 1
function f<T!>(x: T!, y: T): T!; // T: not nullable type, x: not-null, y: null, f: not-null
function f<T!>(x: T!, y: T): T;  // T: not nullable type, x: not-null, y: null, f: null

// Generics constraint option 2
function f<T!>(x: T, y: T | null): T; // same as option 1.1
function f<T!>(x: T, y: T | null): T | null;  // same as option 1.2

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

تعد القيم الحرفية جيدة ولكنها ليست مرضية للغاية أيضًا: var x: { name: string }! ، خاصة إذا كانت تمتد لأكثر من سطر واحد :(

مزيد من الأمثلة:
صفيف من الأرقام غير الفارغة number![]
ليست مصفوفة أرقام فارغة: number[]!
لا شيء يمكن أن يكون خاليًا: number![]!
علم الأجيال: Array<number!>[]

2 و 3 و 5) أوافق على التغيير في الاقتراح ، ويبدو أنه سيعمل.
6)

function f<T>() : number | T; // f can be null
function f<T>() : number! | T; // f is nullable if T is nullable

أفترض أنك تقصد هنا أن f يمكن أن ترجع قيمة خالية ، ولا يمكن إسناد f إلى قيمة خالية.

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

var x = ! & number | string;

للدلالة على أن أيا منهما غير باطل.


بالنسبة للكتابة الحرفية ، أود أن أقول إنه يمكنك وضع الدوي في بداية التعبير الحرفي وكذلك في النهاية ، حيث سيكون التعامل مع var x: !{name: string} أسهل في رأيي من الحصول عليه في النهاية.

@ jods4 هل قرأت عن الإصلاح المقترح "

تضمين التغريدة
نعم بالطبع ، كانت كلمة "is nullable" صياغة سيئة لعبارة "can return null".

مما يجعلني أعتقد أن function f() {} هو في الواقع إعلان f ، والذي يمكن تعيينه لاحقًا ... هل نريد التعبير عن أن f لن يتم تعيينه أبدًا على قيمة خالية ؟ مثل function f!() {} ؟ يبدو أن هذا يذهب بعيدا جدا في رأيي. أعتقد أن أداة فحص الحروف يجب أن تفترض أن هذا هو الحال دائمًا. هناك حالات أخرى غير شائعة لا يمكنها التعامل معها على أي حال.

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

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

class C {
  name! = "jods";  // Field inferred string, but marked not nullable.
  // Maybe we could do that instead, which works with "!T" convention:
  name : ! = "jods";
  // That kind of make sense with the proposed <!> cast.
}

تضمين التغريدة
نعم رأيت ذلك.

أولاً ، لست متأكدًا من أنه حقاً يحل المشكلة المطروحة. إذن مع تعريفك الجديد الذي يرتبط الحمل الزائد بـ fn(null) ؟ العنصر "المُنشأ" الذي يُرجع number ؟ ألا يتعارض ذلك مع حقيقة أن الحمولة الزائدة الثانية تقبل أيضًا null ولكنها تُرجع string ؟ أم أنه خطأ في المترجم لأنك لا تستطيع أداء دقة التحميل الزائد؟

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

ولكن إذا تمكنت من إثبات أن الكود سيتم تجميعه وتشغيله بشكل صحيح في جميع الحالات ، فأنا جميعًا من أجله!

@ jods4

في كلتا الحالتين سيتم استخدام الحمل الزائد الثاني

function fn(x: string): number;
function fn(x: number|null): string; 

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

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

تضمين التغريدة
إذن ما هو الأمر مع function fn(x: null): number; الضمني؟ إذا كان التحميل الزائد الثاني له الأسبقية دائمًا ، بغض النظر عن العلامة المستخدمة ، فإن هذا "الإصلاح" بالكامل ليس له أي تأثير على الإطلاق؟

سؤال آخر: اليوم جميع تعريفات lib لها سلوك "مسموح به لا شيء". لذلك حتى يتم تحديثها _all_ ، يجب أن أستخدم علامة "فارغة ضمنيًا". الآن مع تشغيل هذه العلامة ، كيف يمكنني إعلان وظيفة تأخذ معلمة غير فارغة في مشروعي؟

// null by default flag turned on because of 3rd party libs.
function (x: string)   // <- how do I declare this not null?
{ return x.toUpperCase(); }

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

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

دعنا نقول أن هناك مكتبة تحسب عدد الكلمات لسلسلة. إعلانها هو

declare function wordCount(s: string): number;

لنفترض أن التعليمات البرمجية الخاصة بك تستخدم هذه الوظيفة على النحو التالي:

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

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

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

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

دعنا الآن نلقي نظرة على تنفيذ wordCount

function wordCount(s) {
  if (s == '') return null;
  return s.split(' ').length
}

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

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

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

ومع ذلك ، سنكون قادرين الآن على القيام بعمل أفضل: سنتمكن من كتابة تعريف نوع محسن لـ wordCount:

declare function wordCount(s:string):string|null;

واحصل على تحذير من المترجم كلما حاولنا استخدام wordCount بدون التحقق من قيمة الإرجاع الخالية.

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


(1): يوجد بالفعل تحسين هنا حتى بدون تحسين تعريف النوع. باستخدام تعريفات النوع الجديد ، لن تتمكن من تمرير القيمة الفارغة بطريق الخطأ إلى sumWordcounts والحصول على استثناء مؤشر فارغ عندما تحاول الدالة wordCount إلى .split() هذا فارغًا.

sumWordcounts(null, 'a'); // error after the change

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

ستظل الأمور على ما يرام بالنسبة للحجج الاختيارية:

declare function f(a: string, b?:string); 

ولكن ليس للحجج التي ليست اختيارية ويمكن أن تكون خالية

declare function f(a: string, b:string); // a can actually be null

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

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

الإصلاح بالتأكيد له تأثير. يجعل السلوك مع وبدون العلم متسقًا.

بأي طريقة يمكنك إعطاء مثال كامل؟ قلت أيضا:

في كلتا الحالتين سيتم استخدام الحمل الزائد الثاني

ما ، بالنسبة لي ، يعني أن الإصلاح ليس له أي تأثير؟

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

نعم ، يتم تجميعها بدون أخطاء في كلتا الحالتين ، ولكن ليس لنفس النتيجة. sumWordCounts() كـ number! في حالة واحدة و number? في الحالة الثانية. إن تغيير دلالات الكود باستخدام رمز التبديل أمر غير مرغوب فيه للغاية. كما تم توضيحه من قبل ، يمكن أن يكون لهذا التغيير تأثيرات عالمية ، على سبيل المثال على دقة التحميل الزائد.

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

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

لا يسري التغيير في السلوك إلا عند تعيين قيمة فارغة (أو ترك المتغيرات غير مهيأة) ثم محاولة تمرير هذه القيم إلى دالة مثل المذكورة أعلاه.

لست متأكدًا من فهمي ، لأن "الوظيفة مثل ما ورد أعلاه" تقبل بالفعل القيم الخالية ...

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

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

هذا مثال وكيف أرغب في أن يتصرف المترجم:

افترض مكتبة ذات وظائف yes(): string! لا تُرجع أبدًا قيمة فارغة و no(): string? قد ترجع قيمة خالية.

تعريف .d.ts هو:

declare function yes(): string;
declare function no(): string;

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

باستخدام المُعدِّل غير الفارغ المقترح ! أعلاه ، يمكنني القيام بذلك:

function x(s: string!) {  // inferred : string, could be explicit if we want to
  return s.length === 0 ? null : s;  // no error here as s is declared not null
}

x(no());  // error: x called with a possibly null parameter
y(<!>yes());  // no error because of not null cast. When .d.ts is updated the cast can be dropped.

كيف سيعمل ذلك مع فكرتك؟

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

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

spion نظرًا لأن فكرتك تتضمن أعلام المترجم ، فيجب عليك توثيق سلوك TS لكل جزء من التعليمات البرمجية باستخدام مجموعة العلم وعدم ضبطها.

إليك جوهر علامة !T not-null:
https://gist.github.com/jods4/cb31547f972f8c6bbc8b

إنها في الغالب هي نفسها كما في تعليقي أعلاه ، مع الاختلافات التالية:

  • لقد لاحظت أن الجانب "غير الفارغ" لمعلمات الوظيفة يمكن الاستدلال عليه بأمان بواسطة المترجم (بفضل spion لهذه البصيرة).
  • لقد قمت بتضمين تعليقات Griffork ، ولا سيما حول استنتاج قيم الإرجاع وحاولت !T بدلاً من T! .
  • أضفت بضعة أقسام ، على سبيل المثال حول سلوك المُنشئ.

لا تتردد في التعليق والتقسيم وإنشاء مقترحات بديلة ( ?T ).
دعونا نحاول المضي قدما في هذا.

@ jods4

في كلتا الحالتين سيتم استخدام الحمل الزائد الثاني

ما ، بالنسبة لي ، يعني أن الإصلاح ليس له أي تأثير؟

  1. وهذا يعني أنه تم تغيير دقة التحميل الزائد لجعل دلالات اللغة متسقة لكلتا العلامتين.

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

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

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

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

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

لست متأكدًا من فهمي ، لأن "الوظيفة مثل ما ورد أعلاه" تقبل بالفعل القيم الخالية ...

دعنا نلقي نظرة على هذه الوظيفة:

function sumWordcounts(s1:string, s2:string) {
  return wordCount(s1) + wordCount(s2);
}

تحت علامة المترجم الجديدة لن تتمكن من تسميتها بقيم فارغة مثل sumWordCounts(null, null); وهذا هو الاختلاف الوحيد. سيتم تجميع الدالة نفسها لأن تعريف wordCounts يشير إلى أنها تأخذ سلسلة وتعيد رقمًا.

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

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

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


حسنًا ، لذا فإن تعريفات الأنواع الحالية هي

declare function yes(): string;
declare function no(): string;

ولنقل أن الكود الخاص بك قد تمت كتابته قبل إضافة الميزة أيضًا:

function x(s: string) {
  return s.length === 0 ? null : s;
}

في ظل المترجم الجديد ، سيكون السلوك مطابقًا تمامًا للسلوك القديم

x(no());  // no error
x(yes());  // no error

ما لم تجرب شيئًا يعرف المترجم أنه قد يكون فارغًا ، مثل نتيجة x ()

x(x(no())) // error, x(something) may be null

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

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

خلاصة القول هي أن نظام الكتابة في TypeScript خاطئ حاليًا. لها تفسير مزدوج للقيم الفارغة:

عندما نحاول إسناد قيمة خالية لبعض المتغيرات من النوع T ، أو تمرير القيمة null كمتغير حيث يكون النوع T مطلوبًا ، فإنه يتصرف كما لو كان النوع T|null .

عندما نحاول استخدام قيمة من النوع T ، فإنها تعمل كما لو كان النوع T ولا توجد إمكانية لقيمة خالية.

يقترح اقتراحك معاملة جميع الأنواع العادية T أنها T|null دائمًا ؛ أقترح معاملتهم على أنهم T . كلاهما يغير الدلالات. كلاهما يجعل المترجم يتصرف "بشكل صحيح"

حجتي هي أن المتغير "فقط T " أقل إيلامًا مما يبدو ويتوافق مع الغالبية العظمى من الكود.

تحرير: لقد أدركت للتو أن اقتراحك قد يكون الاستمرار في معالجة الأنواع بدون "!" أو "؟" بنفس الطريقة كما كان من قبل. هذا متوافق مع الإصدارات السابقة ، نعم ، ولكن هناك الكثير من العمل للحصول على أي فوائد (حيث أن الغالبية العظمى من الكود لا تتعامل ببساطة مع القيم الفارغة بخلاف التحقق / الرمي.

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

1.إنه يعني أنه تم تغيير دقة التحميل الزائد لجعل دلالات اللغة متسقة لكلتا العلامتين.

إنك تقول فقط نفس الحقيقة ولكن لا يزال من غير الواضح بالنسبة لي ما هي الحالة التي تتناولها وبأي طريقة.
لهذا طلبت مثالًا ملموسًا.

1. لا يوجد number! في اقتراحي. سيكون النوع ببساطة number في كلتا الحالتين.

هذا غير صحيح ، أعلم أن بناء الجملة مختلف لكن المفاهيم الأساسية هي نفسها. عندما أقول number! أؤكد على نوع رقم غير فارغ ، والذي سيكون في اقتراحك ببساطة number . والحالة الثانية لن تكون number في حالتك ، ولكن number | null .

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

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

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

جدل جدا.

هناك بعض الوظائف هنا وهناك باستخدام الوسائط الاختيارية.

أكثر قابلية للجدل. مكتبات JS مليئة بمثل هذه الأمثلة.

، والتي يمكن التعرف عليها ويمكن تشكيلها على الأرجح وفقًا للمفتاح الجديد

قدم عرضًا أكثر واقعية لأن هذا اختصار ضخم. أعتقد أنه يمكنني معرفة إلى أين يتجه هذا للحصول على رمز JS الفعلي ، ولكن كيف يمكنك "التعرف" على وسيطة اختيارية داخل declare function(x: {}); أو داخل interface ؟

ليس في كثير من الأحيان أن الوظائف ترجع القيم الفارغة مثل المثال الخاص بي.

مرة أخرى جدل. تقوم العديد من الدالات بإرجاع قيمة null أو undefined : find (عندما لا يتم العثور على العنصر) ، getError (عند عدم وجود خطأ) وهكذا ... إذا كنت تريد المزيد من الأمثلة ، فما عليك سوى إلقاء نظرة على واجهات برمجة التطبيقات القياسية للمتصفح ، وستجد _plenty_.

ما أقوله هو أن الغالبية العظمى من تعريفات الأنواع ستبقى صحيحة.

كما يمكنك أن تقول من تعليقاتي السابقة ، لست مقتنعًا بذلك.

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

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

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

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

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

  • ستحصل على الكثير من مزايا التحليل الفارغ ، حتى بدون التعليقات التوضيحية (إلى حد كبير بالطريقة نفسها التي تحصل عليها في مترجم Flow).
  • سوف ينكسر شيء واحد : استخدام القيمة المرجعة لدالة مكتبة تعرف أنها لا تؤدي إلى إرجاع قيمة خالية.
declare function f(): string; // existing declaration, but f will never return null.
var x = f();
x.toUpperCase();  // error, possibly null reference.

الإصلاح طويل المدى هو تحديث تعريفات المكتبة لتكون أكثر دقة: declare function f(): !string;
الإصلاح قصير المدى هو إضافة طاقم غير فارغ: var x = <!>f(); .
ولمساعدة المشاريع الكبيرة التي تحتوي على الكثير من التبعيات على الترقية بسهولة أكبر ، أقترح إضافة علامة مترجم مشابه لـ "لا يوجد أي ضمني": "معاملة المراجع الفارغة المحتملة كتحذير". هذا يعني أنه يمكنك استخدام المترجم الجديد والانتظار حتى يتم تحديث تعريف المكتبات. ملاحظة: على عكس اقتراحك ، لا يغير هذا العلم دلالات المترجم. إنه يغير فقط ما تم الإبلاغ عنه كخطأ.

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

هناك الكثير من المناقشات حول أشياء مختلفة تحدث في هذا الموضوع الذي يزيد عن 130 تعليقًا.

أقترح أن نواصل مناقشة _general_ هنا.

لإجراء مناقشات حول المقترحات الملموسة التي قد تنفذ أنواعًا غير فارغة في TS ، أقترح أن نفتح قضايا جديدة ، كل منها يتعلق بمقترح تصميم واحد. لقد أنشأت 1800 # لمناقشة بناء الجملة !T .

spion أقترح عليك إنشاء مشكلة

كثير من الناس يريدون أنواع لا فارغة. إنه سهل في اللغات الجديدة (على سبيل المثال Rust) ولكن من الصعب جدًا تعديله في اللغات الحالية (لطالما طلب الناس مراجع غير فارغة في .net لفترة طويلة - ولا أعتقد أننا سنحصل عليها أبدًا معهم). يُظهر النظر إلى التعليقات هنا أنها مشكلة غير تافهة.
لقد أقنعني Flow أنه يمكن القيام بذلك من أجل TS ، فلنحاول تحقيق ذلك!

@ jods4 يؤسفني أن أقول ذلك ولكن
أنا أتفق مع حقيقة أن المناقشة لن ​​تسير في أي مكان ، وأن 130 تعليقًا ربما يكون أكثر من اللازم. ولكن ربما يرجع ذلك إلى وجود مجموعتين من الأشخاص يناقشان هنا:

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

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

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

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

كما يمكنك أن تقول من تعليقاتي السابقة ، لست مقتنعًا بذلك.

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

الآن لا يمكنك التعبير عن ذلك

declare function err():string;

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

بعد هذا التغيير ، ستكون قادرًا على ذلك ، عن طريق تغيير string إلى ?string أو string|null

ولكن إذا لم تفعل ذلك ، فلن تفقد أي شيء كان لديك قبل التغيير:

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

الفرق هو: لا يمكنك فرض التحقق من القيمة الفارغة قبل التغيير ، ولكن يمكنك _ بعد ذلك (تدريجيًا ، أولاً في التعريفات التي تهتم بها كثيرًا ، ثم في أماكن أخرى)

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

spion ، أنا جميعًا من أجل التصريح عن

و fdecampredon كان طلبي الوحيد هو عدم إجراء تغيير

أظن أن spion في

تضمين التغريدة
ما أفهمه من تعليقك الأخير يختلف اختلافًا كبيرًا عما فهمته من قبل ...

لذا فإن : string هو "نوع السلسلة القديم الذي لن يتم التحقق منه أبدًا من عدم وجود قيمة فارغة ، مثل <any> لم يتم التحقق منه أبدًا للتأكد من صحة النوع".

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

هذا مثير للاهتمام ومن الصعب فهم كل الآثار المترتبة عليه. سأفكر في الأمر لبعض الوقت.

السؤال الأول الذي يتبادر إلى الذهن هو كيف تضع قيودًا على قيم الإدخال (مثل معلمات الوظيفة):

declare f(x: string): void;
f(null);

هل هذا جيد أم خطأ؟ إذا كان الأمر على ما يرام ، فكيف يمكنني أن أخطئ.

_ ما زلت أعتقد أن أي فكرة داخل هذه المناقشة قد ضاعت ، ألا تريد فتح قضية جديدة بفكرتك هذه؟ يمكننا مناقشته هناك.

jods التي سيكون خطأ:

declare f(x: string): void; //string must not be null.
declare f(x: string|null): void; //string may be null (not sure about undefined here).
declare f(x?: string): void; //I assume x may be null or undefined.

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

fdecampredon أعلم أن هناك الكثير من

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

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

إذا فهمت بشكل صحيح ، فهذا أمر جيد للمشاريع الجديدة (بمجرد تحديث .d.ts ) ولكنه مؤلم جدًا للكود الحالي.

Griffork مشكلتي مع هذه القضية الفردية

اقتراحspion الأصلي لا يحتوي على علم سحري. لقد كان تغييرًا أساسيًا في كيفية عمل الكتابة المطبوعة.

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

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

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

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

وأيضًا @ jods4 هو مجرد تغيير

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

فيما يلي مثال لما قد تبدو عليه عملية "الترقية" لمقترحات @ jods4 و spion (بافتراض تشغيل أي علامات ضرورية):

function a(arg1: string): string;
a("mystr").toLowerCase(); //errors in <strong i="11">@jods4</strong>'s proposal because a may return null.
a("mystr").toLowerCase(); //fine in <strong i="12">@spion</strong>'s proposal.

a(null).toLowerCase(); //fine in <strong i="13">@jods4</strong>'s proposal because a may accept null.
a(null).toLowerCase(); //errors in <strong i="14">@spion</strong>'s proposal since a doesn't accept null.

في النهاية كلاهما يكسر التغييرات. كلاهما مؤلم بنفس القدر عند تصحيح الأخطاء ، مع حدوث أخطاء عند الاستخدام المتغير بدلاً من إعلان المتغير. الفرق هو أن هناك خطأ واحدًا عندما يتم تخصيص قيمة خالية لشيء ما (spion) ، والأخطاء الأخرى عندما لا يتم تخصيص قيمة خالية لشيء ما (@ jods4). أنا شخصياً أقوم بتعيين قيمة خالية للأشياء كثيرًا أقل من عندما لا أخصص قيمة خالية للأشياء ، لذا فإن

أشياء أخرى أحبها:

  • أنواع المتغيرات لا تتغير ديناميكيًا. إذا أردت أن أستخدم التدفق:
var s: string;
s.toUpperCase(); // error
var t = (s || "test").toUpperCase(); // ok. Inferred t: string
yes(t);  // ok
t = no();
yes(t);  // error

أنا شخصياً أرغب في حدوث خطأ إذا اتصلت بـ لا () ، وليس تغييرًا ضمنيًا للنوع.

  • العناصر الفارغة التي تحتوي على كلمة خالية ، بدلاً من بعض الرموز ، يسهل قراءتها وأقل تذكرًا. هل حقا ! يجب استخدامه لـ "not" not "not-null". أكره الاضطرار إلى تذكر ما يفعله الرمز ، تمامًا كما أكره الاختصارات ، لا يمكنني تذكرها أبدًا وهي تبطئ بشكل كبير من سرعة عملي.

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

Griffork بصراحة قرأت كل ذلك ، لكنني أشك في أن الكثير من الناس

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

@ jods4 يجب أن تكون المناقشات الفرعية غير المحققة متفرعة ، وليس المعلومات الأساسية الفعلية. لكن فات الأوان للعودة وتغيير ذلك الآن.

spion ، أعتقد أن لدي حلًا أبسط لمشكلة RyanCavanaugh مع علامة "-nonImplicitNull" ، الفكرة بسيطة جدًا ولا يتعين علينا السماح بـ ?type type|null أو type|undefined بدون هذا العلم.

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

@ jods4

declare f(x: string): void;
f(null);

عند استخدام علامة --noImplicitNull المقترحة ، نعم ، سيكون هذا خطأ.

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

لقد لاحظت أن اقتراح !T سيؤدي في الواقع إلى كسر الكثير من التعليمات البرمجية أيضًا. ليس لأن دلالات الأنواع الحالية قد تغيرت ولكن لأن التحليل الجديد سيثير أخطاء في الكثير من الأماكن.

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

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

أتساءل ما هو موقف فريق TS الرسمي فيما يتعلق بالجانب الفاصل لهذا مقابل فوائده.

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

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

: +1: تم الكشف عن شخص عاقل: RyanCavanaugh

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

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

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

أتخيل أن هذا لن يكون مختلفًا تمامًا عن كل عمليات الكتابة العادية وإضافة علامة - noImplicitAny.

تضمين التغريدة
النصف الأول من الحزمة (أضف نوع null وحظر عدم الرجوع بدون حارس) غير متوافق بنسبة 100٪. سوف تكسر كود أكثر مما تعتقد. ضع في اعتبارك هذا:

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

interface Array<T> {
  find(predicate: (T) => bool) : T | null;
  pop() : T | null;
}
interface Storage {
  getItem(key: string) : any | null;
}

هناك العديد من واجهات برمجة التطبيقات: find تُرجع undefined إذا لم يكن هناك عنصر مصفوفة يتطابق مع المسند ، pop تُرجع undefined إذا كانت المصفوفة فارغة ، localStorage.getItem أو sessionStorage.getItem بإرجاع null إذا لم يتم العثور على المفتاح.

الشفرة التالية شرعية وتم تجميعها بشكل مثالي من قبل. سوف ينكسر الآن مع وجود خطأ:

var xs: string[];
if (xs.length > 0) return xs.pop().trim();  // error xs.pop() may be undefined (false positive)

var items : { id: number }[];
var selectedId : number;
// Assume we are sure selectedId is amongst items
var selectedItem = items.find(x => x.id === selectedId);
selectedItem.toString(); // error selectedItem may be undefined (false positive)

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

سيشمل ذلك بعض الأخطاء الفعلية ، بالتأكيد. لكنها ستتضمن أيضًا الكثير من الإيجابيات الكاذبة وهذه تغييرات كبيرة. في 100K + LOC هذه ليست ترقية تافهة للمترجم.

ربما يعني هذا أن الطريقة الوحيدة التي ستنجح هي: الشفرة الجديدة المكتوبة من البداية أو الشفرة القديمة المُرحّلة تستفيد استفادة كاملة من التحقق من القيمة الفارغة ؛ الكود القديم ليس له أي فائدة على الإطلاق. يتم دفع هذا بواسطة خيار مترجم (--enableNullAnalysis) وبدون مسار "انتقال" أو "تقدمي".

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

تعديل
@ jods4 لماذا استخدمت رمزًا غير آمن كمثال في حين أن هذا هو بالضبط نوع الكود الذي قد يتسبب في حدوث خطأ نحاول اكتشافه بإجراء هذا التغيير؟

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

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

_ ستؤدي الكتابة الجديدة إلى كسر الكود القديم ، وهذا سيحدث على أي حال.

ليست هذه هي القضية. باستثناء الظروف الاستثنائية ، لن نضيف ميزات جديدة تكسر الكود الحالي. لم تتم إضافة العوامل الوراثية والنقابات إلى lib.d.ts بطريقة تتطلب من مستهلكي lib.d.ts تحديث قاعدة التعليمات البرمجية الخاصة بهم من أجل استخدام الإصدار الجديد من المترجم. نعم ، يمكن للأشخاص اختيار استخدام إصدار أقدم من المكتبة ، ولكن ليس الأمر ببساطة أننا سنأخذ تغييرات اللغة التي تكسر الكثير من التعليمات البرمجية الموجودة (كما هو الحال بالنسبة لجميع اللغات الرئيسية بشكل أساسي). سيكون هناك استثناء عرضي لهذا (https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes) لكنها ستكون قليلة ومتباعدة ، في أغلب الأحيان إذا اعتقدنا أن الكود الذي نكسر حشرة.

لكن يمكنك دائمًا تجميد lib.d.ts لمشروع أو الحصول على واحد قديم

إن تجميد lib.d.ts (وأي ملفات .d.ts أخرى قد أستخدمها راجع للشغل) له آثار قوية للغاية. هذا يعني أنه لا يمكنني الحصول على أي تحديثات على libs التي أستخدمها أو واجهات برمجة تطبيقات HTML الجديدة. هذا ليس شيئا يمكن الاستخفاف به.

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

لدى JS تقليد إرجاع undefined (أو أحيانًا null ) في كثير من الحالات التي قد تؤدي إلى حدوث خطأ في اللغات الأخرى. مثال على ذلك هو Array.prototype.pop . في C # ظهرت فرقعة من مكدس فارغ. لذلك يمكنك القول أنه يعرض دائمًا قيمة صالحة. في جافا سكريبت ظهرت مصفوفة فارغة بإرجاع undefined . إذا كان نظام الكتابة الخاص بك صارمًا بشأن ذلك ، فعليك الاعتناء به بطريقة ما.

كنت أعلم أنك ستجيب على ذلك ، ولهذا السبب كتبت أمثلة شرعية ورمز عمل. في قواعد التعليمات البرمجية الكبيرة ، ستجد الكثير من الأمثلة حيث قد يُرجع api قيمة فارغة في بعض المواقف ، لكنك تعلم أنه آمن في حالتك المحددة (وبالتالي تتجاهل أي فحوصات أمان).

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

class MicroTasks {
  queue: Array<() => void>;

  flushQueue() {
    while (queue.length > 0) {
      let task = queue.pop();
      task();  // error possible null dereference (not!)
     }
  }
}

هناك الكثير من الحالات مثل هذه.

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

dquirk Fair ، إذن أعتقد أن هذه الميزة لا يمكن تنفيذها أبدًا.
@ jods4 أفهم ما تقوله ، لكن لا يمكن تنفيذ nullables بدون كسر هذا الرمز.

حزين ، هل يجب أن أغلق هذه المسألة أخيرًا؟

من المحتمل.

أعتقد أننا إذا ابتعدنا عن حجة لاغية وغير قابلة للإلغاء ...

يمكن معالجة المشكلة (أو التخفيف من حدتها) بطريقة غير تدخلية مع مجموعة من الميزات الجديدة:

  • قدم الرمز ?<type> null-typeguard ?<type>
    إذا تم إضافة تعليق توضيحي على نوع ما بهذا الرمز ، فمن الخطأ الوصول إليه مباشرة

تيبسكريبت
فار فو:؟ سلسلة ؛

foo.indexOf ('s') ؛ // خطأ
foo && foo.indexOf ('s)؛ // تمام
""

  • قدم العلم --nouseofunassignedlocalvar

تيبسكريبت
فار فو: سلسلة ؛

foo.indexOf ('s') ؛ // خطأ

foo = "شريط" ؛

foo.indexOf ('s') ؛ // حسنا
""

ملاحظة تاريخية مثيرة للاهتمام : أثناء محاولتي العثور على مشكلة ذات صلة بهذا الأمر ، صادفت مشكلة قديمة في codeplex تشير إلى خيار مترجم --cflowu مرة أخرى في فبراير 2013. RyanCavanaugh ، أتساءل ماذا حدث لتلك الراية؟

  • قدِّم عامل الملاحة الآمن رقم 16

TypeScript var x = { y: { z: null, q: undefined } }; console.log(x?.y?.z?.foo); // Should print 'null'

مجتمعة ، ستساعد هذه الميزات في اعتراض المزيد من الأخطاء حول استخدام القيمة null دون التسبب في حدوث انهيار عصبي للجميع.

NoelAbrahams :
مقترحك الأول هو بالضبط نفس الاقتراح الأخير ، أنت فقط تستخدم؟ بدلاً من | null (اقرأ منشور @ jods4 حول مشاكل تحديث lib.d.ts وتعطيل التغييرات).

يحتوي عرضك الثاني على مشكلة تم تناولها مسبقًا بواسطة RyanCavanaugh (أعلاه):

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

أنا متأكد تمامًا من أنه تم اقتراح علم لا يختلف في وقت سابق ، ثم تم التخلي عنه بسبب تعليق RyanCavanaugh .

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

هل يمكن تنفيذ أحد المقترحات المقترحة ، وعدم تحديث lib.d.ts لاستخدام العناصر الفارغة (والمحتويات الأخرى)؟ ثم يمكن للمجتمع الحفاظ على / استخدام الإصدارات الخاصة بهم مع nullables إذا أرادوا؟

تعديل:
على وجه التحديد ، تحتوي جميع libs على أحدث المعلومات ، ولكن لم يتم تحديث كتابتها لتتطلب أدوات طباعة.

RyanCavanaugh سأعود

dquirk تعريفات لن تحتاج إلى تحديث. يمكن أن تظل "مخطئة" وستكون متوافقة في الغالب مع الإصدارات السابقة.

على أي حال ، يبدو أن هذه القضية هي قضية خاسرة.

بالمناسبة ، كان هناك مترجم معدل بواسطة MSR قام بهذا من بين أشياء أخرى - هل هناك أي أوراق بحثية متاحة مع النتائج التي توصلوا إليها؟ تحرير: وجدته: http://research.microsoft.com/apps/pubs/؟id=224900 لكن لسوء الحظ ، لست متأكدًا من ارتباطه.

Griffork ، نعم ، أنا متأكد من أنه تمت مناقشة كل شيء تقريبًا بشكل أو بآخر - نظرًا لطول المناقشة. تلخيصي هو ملخص عملي حول التخفيف من المشاكل حول null من خلال تقديم عدد من الميزات ذات الصلة.

عدم إضافة شيكات جديدة فارغة أو غير محددة في كل مكان

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

أعتقد أيضًا أن احتمالية حدوث ذلك في TS منخفضة ... :(

الحل الوحيد الذي يمكنني التفكير فيه هو جعل السلامة الفارغة علامة مجمعة اختيارية. على سبيل المثال ، أدخل بناء الجملة الجديد T | null أو ?T لكن لا تظهر أي خطأ _ أي ما لم يتم تفعيل المشروع. لذلك تحصل المشاريع الجديدة على الفوائد وكذلك تفعل المشاريع القديمة التي تختار جعل كودها متوافقًا مع الميزة الجديدة. لا تحصل قواعد الرموز الكبيرة التي لا يمكن تكييفها بسهولة على هذه الميزة.

ومع ذلك ، حتى مع وجود العديد من المشكلات المتبقية لجعل هذه الميزة تطير ...

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

@ jods4 الذي لديه نفس المشكلة مع تعليق RyanCavanaugh الذي نقلته أعلاه (خطأ في التخصيصات بسبب مشكلة في مكان آخر عند تغيير العلم).

مرة أخرى ، أسهل طريقة هي تنفيذ أحد المقترحات الأخرى (ربما spion 's) وعدم إضافة الأنواع الجديدة إلى .d.ts.

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

على سبيل المثال ، إذا استخدمنا تصميم "not null" !T ، فلا أعتقد أن لدينا هذه المشكلة. لكن هذا أبعد ما يكون عن حل المشكلة.

يجب أن أشير أيضًا إلى أنه في حين أن T | null هو تغيير فاصل ?T ليس كذلك.

@ jods4 كان لدي انطباع بأن "تغيير الدلالات يتضمن" تغيير القدرة على التخصيص "، وفي كلتا الحالتين لا تزال مخاوفي (وربما

NoelAbrahams كيف؟
تي | تتطلب القيمة null وجود حرف ،؟ T تتطلب أداة طباعة ، فهما نفس الشيء بأسماء / رموز مختلفة.
نعم ، يمكنك "تشغيل" كلاهما بعلامة.
أنا لا أرى الفرق؟

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

يكسر التعليق التوضيحي T|null الكود الحالي لأنه يقدم بيانًا حول ما إذا كانت الأنواع لاغية أم لا بشكل افتراضي.

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

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

تضمين التغريدة
جميع مقترحات فرض العدم هي كسر التغييرات. حتى ?T الذي وصفته. لقد عرضت عدة أمثلة لماذا بعض التعليقات أعلاه.

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

أنا في الواقع بخير نوعًا ما مع هذا. مع TS 1.5 سأكون قادرًا على الحصول على ما أريد:

function isVoid(item:any): item is void { return item == null; }
declare externalUnsafeFunction(...):string|void

function test() {
  var res = externalUnsafeFunction(...);
  var words = res.split(' '); // error
  if (!isVoid(res)) {
    var words = res.split(' '); // ok
  }
}

الآن تم فرض الاختيار isVoid على قيم الإرجاع لـ externalUnsafeFunction أو أي دالة أخرى أو تعريف دالة حيث أقوم بإضافة |void إلى نوع الإرجاع.

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

نظرًا لأن NoelAbrahams ناقشنا بالفعل أنه حتى مع وجود نوع فارغ ، فلا يزال يتعين السماح لـ null بأن يلقي ضمنيًا إلى أي نوع آخر ما لم يغير ذلك علم المترجم المستقبلي.

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

وأنا شخصياً أكره فكرة استخدام الرموز لتمثيل الأنواع عندما يمكننا استخدام الكلمات بدلاً من ذلك.

spion هذه نقطة جيدة في الواقع. إذا كان هذا يعمل في TS حاليًا ، فيمكن القول إن جميع ملفات d.ts المضمنة "خاطئة" بالفعل ، فإن إضافة النوع الفارغ كما اقترحته مع التعديلات المقترحة لن يغير أي شيء.

في الواقع ، نظرًا لأنه يمكن تحقيقه إلى حد ما بالفعل ، سأقترح أن نبدأ في استخدام هذا الأسبوع على رئيسي.

سأحذر من أننا لا نرى بأي حال من الأحوال T|void كتركيب لنخشى كسره في المستقبل. يكاد يكون هذا هراء.

RyanCavanaugh إنه مجرد هراء مثل void === غير محدد (قيمة

تنهد.

الكود الخاص بنا أكبر من أن يبدأ بالاعتماد على T|void إذا كان سيتعطل قريبًا ولن يتم استبداله. ولن أتمكن من إقناع المبرمجين الآخرين باستخدامه إذا كان من الممكن أن ينكسر أي أسبوع.

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

تضمين التغريدة في أي طريق؟ وماذا تقترح للتعبير عن أنواع لاغية يجب حظرها لأي نوع من الاستهلاك قبل الشيك الفارغ / غير المحدد؟

لدي بالفعل الكثير من المكتبات التي قد تستفيد من ذلك.

Griffork ، لن يكون من الممكن إزالة void بأمان من الاتحاد قبل 1.5 على أي حال ، ليس بدون حراس من النوع المحدد من قبل المستخدم ، لذا فإن استخدام هذا سيكون ممكنًا فقط بعد 1.5 (إذا أصبح ذلك ممكنًا على الإطلاق)

هراء مثل هل تكتب هذا الرمز؟

var foo: void;
var bar: void = doStuff();

بالطبع لا. إذن ما معنى إضافة void إلى اتحاد الأنواع الممكنة؟ لاحظ هذا الجزء من مواصفات اللغة الموجودة منذ فترة طويلة في القسم الذي يصف The Void Type (3.2.4):

_ ملاحظة: قد نفكر في عدم السماح بالتصريح عن متغيرات من النوع Void لأنها لا تخدم أي غرض مفيد. ومع ذلك ، نظرًا لأن Void مسموح به كوسيطة نوع لنوع أو وظيفة عامة ، فليس من المجدي عدم السماح بخصائص أو معلمات Void.

هذا السؤال:

_ماذا تقترح للتعبير عن أنواع لاغية يجب حظرها لأي نوع من الاستهلاك قبل الشيك الفارغ / غير المحدد؟ _

هو ما هو موضوع كامل حول. يشير رايان فقط إلى أن string|void هو هراء وفقًا لدلالات اللغة الحالية ، وبالتالي ليس من المعقول بالضرورة الاعتماد على دلالات لا معنى لها.

الكود الذي كتبته أعلاه هو afaik صالح تمامًا TS 1.5 ، أليس كذلك؟

سأعطي مثالًا بالتأكيد ليس هراء. لدينا وظيفة مكتبة قاعدة بيانات (nodejs) نسميها get() غرار Linq's Single () التي تُرجع للأسف Promise<null> بدلاً من ذلك تلقي خطأ (وعد مرفوض) عندما لا يتم العثور على العنصر. يتم استخدامه في جميع أنحاء قاعدة الرموز الخاصة بنا في آلاف الأماكن ومن غير المحتمل أن يتم استبداله أو إزالته قريبًا. أريد أن أكتب تعريفًا للنوع يجبرني والمطورين الآخرين على استخدام نوع حارس قبل استهلاك القيمة ، لأن لدينا العشرات من الأخطاء التي يصعب تتبعها والناشئة عن قيمة فارغة تتحرك بعيدًا في قاعدة الشفرة قبل أن يتم استهلاكها بشكل غير صحيح.

interface Legacy { 
  get<T>(...):Promise<T|void>
}

function isVoid(val: any): val is void { return val == null; } // also captures undefined

legacy.get(...).then(val => {
  // val.consumeNormally() is a type error here
  if (!isVoid(val)) { 
    val.consumeNormally(); // OK
  }
  else { handle null case }
});

هذا يبدو معقولا تماما بالنسبة لي. تؤدي إضافة void إلى الاتحاد إلى أن تكون جميع العمليات على val غير صالحة ، كما ينبغي. تقوم أداة الكتابة بتضييق النوع عن طريق إزالة |void وترك الجزء T .

ربما لا تأخذ المواصفات تداعيات النقابات الباطلة في الاعتبار؟

لا ترفض المتغيرات الباطلة! تعمل كقيم لنوع الوحدة ويمكن استخدامها
في التعبيرات (على عكس الفراغ في C # الذي يتطلب مجموعتين من كل شيء: واحد
للإجراءات ووظائف واحدة). من فضلك اترك الفراغ وشأنه ، حسنا؟
في 27 كانون الثاني (يناير) 2015 الساعة 8:54 مساءً ، كتب "Gorgi Kosev" [email protected] :

الكود الذي كتبته أعلاه هو afaik صالح تمامًا TS 1.5 ، أليس كذلك؟

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

واجهة قديمة {
احصل على(...):يعد
}
الدالة isVoid (val: any): val باطل {return val == null؛ } // يلتقط أيضًا undefined

legacy.get (...). ثم (val => {
// val.consumeNormally () خطأ
إذا (! isVoid (val)) {
val.consumeNormally () ؛ // موافق
}
آخر {حالة فارغة}
}) ؛

هذا يبدو معقولا تماما بالنسبة لي. هل يمكنك أن تشير إلى أي جزء
كلام فارغ؟

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -71767358
.

ما أقوله هو أن T|void لا معنى له أساسًا في الوقت الحالي لأن لدينا بالفعل قاعدة مفادها أن اتحاد النوع ونوعه الفرعي يكافئ ببساطة النوع الفائق ، على سبيل المثال ، Cat|Animal يعادل Animal . معاملة void كبديل للقيم null / undefined ليست متماسكة لأن null و undefined موجودان بالفعل في مجال T _ لجميع T ، مما يعني أنه يجب أن يكونوا أنواعًا فرعية من T

بمعنى آخر ، T|null|undefined ، إذا كان بإمكانك كتابة ذلك ، فربما يكون عرضة للانهيار بالفعل إلى T . معاملة void كتعويذة لـ null|undefined خطأ لأنه إذا كان هذا هو الحال بالفعل ، لكان المترجم قد انهار T|void إلى T بالفعل.

أود قراءة هذا http://www.typescriptlang.org/Content/TypeScript٪20Language٪20Specification.pdf شخصيًا

القيم الوحيدة الممكنة لنوع الفراغ هي خالية وغير معرفة. النوع Void هو نوع فرعي من أي نوع ونوع فائق من النوعين Null و Undefined ، ولكن بخلاف ذلك ، لا يرتبط Void بجميع الأنواع الأخرى.

كما؛ في T|void ، T على وجه التحديد _not_ نوع فائق من void لأن النوع الفائق الوحيد (وفقًا للمواصفات) هو any .

تحرير: عدم السماح هو قضية مختلفة.

Edit2: أعد قراءة ما قلته ، وسيكون ذلك منطقيًا حرفيًا (بالطريقة التي صاغتها الوثائق باطل) ، ولكن لا معنى لها فيما يُفترض أنه التفسير الشائع (أو الصحيح) للوثائق.

RyanCavanaugh سأستخدم الميزة على أي حال حتى تتعطل ، لأنه في هذه الأثناء من المحتمل أن تساعدني في العثور على الكثير من الأخطاء.

ولا يسعني إلا أن أشير إلى السخرية من القول إن انهيار T|null|undefined إلى T "أمر منطقي" بينما وجود T|void لا يفعل ذلك. (أعلم ، الأمر يتعلق بالمواصفات ، لكن لا يزال ...)

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

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

من المحزن أن أشرح المفارقة ، لكن هنا:

  1. لديك قيمة لا تدعم أي طرق ولا خصائص ، null
  2. تقوم بإنشاء نظام كتابة حيث يتم تخصيص هذه القيمة للمتغيرات من أي نوع ، ومعظمها يدعم أساليب وخصائص مختلفة (مثل أرقام السلاسل أو الكائنات المعقدة var s:string = null )
  3. تقوم بإنشاء نوع void يقبل هذه القيمة بشكل صحيح ولا يسمح بأي طرق أو خصائص ، ونتيجة لذلك لا يسمح T|void بأي طرق أو خصائص.

الادعاء هو أن (3) هراء بينما (2) ليس كذلك. هل ترى الجزء المثير للسخرية هنا؟ إذا لم يكن كذلك ، سأجرب مثالًا آخر:

  1. لديك قيم تدعم فقط بعض الأساليب والخصائص {}
  2. تقوم بإنشاء نظام كتابة حيث يتم تخصيص هذه القيمة لمتغيرات من أي نوع (على سبيل المثال ، لأرقام السلاسل أو الكائنات المعقدة var s:string = {} ) التي تحتوي على مجموعة أكبر بكثير من الأساليب والخصائص
  3. لديك نوع {} يقبل قيم {} بشكل صحيح ولا يسمح بأي طرق أو خصائص بخلاف عدد قليل منها ، و T|{} بطبيعة الحال لا يسمح بأي طرق أو خصائص بخلاف مدمجة منها {}

الآن أيهما هراء ، (2) أم (3)؟

ما هو الفرق الوحيد بين المثالين؟ عدد قليل من الأساليب المضمنة.

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

spion هذا بالضبط ما كنت أفكر فيه.

هنا ، أعتقد أن هذا سيوضح الأمور قليلاً:
حسب المواصفات :


ما هو الفراغ؟

يمثل النوع Void ، المشار إليه بواسطة الكلمة الأساسية الفارغة ، عدم وجود قيمة ويتم استخدامه كنوع إرجاع للوظائف بدون قيمة إرجاع.

لذا ، فإن الفراغ ليس باطلاً | غير محدد.

القيم الوحيدة الممكنة لنوع الفراغ هي خالية وغير معرفة

لذلك ، يمكن التنازل عن null و undefined إلى void (لأنهما قابلان للتنازل إلى حد كبير عن أي شيء آخر).


Null هو نوع.

3.2.5 النوع الفارغ
يتوافق النوع Null مع نوع JavaScript البدائي المسمى بالمثل وهو نوع القيمة الخالية
حرفي.
تشير القيمة الحرفية الفارغة إلى القيمة الواحدة والوحيدة للنوع Null. لا يمكن الإشارة مباشرة إلى النوع Null نفسه.


هو نوع.

3.2.6 النوع غير المحدد
النوع غير المعرّف يتوافق مع نوع JavaScript البدائي المسمى بشكل مشابه وهو نوع الحرف غير المحدد.


يعد الفراغ نوعًا فائقًا فقط من الأنواع _ الفارغة وغير المحددة

. النوع Void هو نوع فرعي من أي نوع ونوع فائق من النوعين Null و Undefined ، ولكن بخلاف ذلك ، لا يرتبط Void بجميع الأنواع الأخرى.


لذا ، فإن jbondc وفقًا لمواصفات لغة الأنواع ، القيمة الفارغة هي قيمة ، القيمة الفارغة هي نوع ، القيمة غير المعرفة هي نوع ، والباطل (الأحرف الصغيرة عندما تكون في الكود) هو نوع من النوع الفائق للغة خالية و غير محدد ولكن _ لا يتم تحويله ضمنيًا إلى أي نوع آخر (باستثناء any ). في الواقع ، تمت كتابة Void خصيصًا بحيث يكون لها سلوك مختلف عن الأنواع Null و Undefined:

فارغ:

النوع Void هو نوع فرعي من أي نوع ونوع فائق من النوعين Null و Undefined ، ولكن بخلاف ذلك ، لا يرتبط Void بجميع الأنواع الأخرى.

باطل:

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

غير معرف:

النوع غير المعرّف هو نوع فرعي لجميع الأنواع. هذا يعني أن القيمة غير المعرفة تعتبر قيمة صالحة لجميع الأنواع الأولية وأنواع الكائنات وأنواع الاتحاد ومعلمات النوع.

أوضح الآن؟

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

jbondc هذه هي المفارقة: بغض النظر عما يقوله مترجم TS ، فإن القيم الخالية وغير المعرفة في JS لن تدعم أبدًا أي طرق أو خصائص (بخلاف طرح الاستثناءات). لذلك ، فإن جعلها قابلة للتخصيص لأي نوع هو هراء ، مثل هذا القدر من الهراء مثل السماح {} القيمة null أو undefined قابلة للتخصيص للجميع ، وبالتالي يمكن تخصيصها لمتغيرات من النوع void ، لذلك هناك القليل من الهراء هناك. وهذه هي المفارقة - أنه (عبر حراس النوع والنقابات) يتحد كلاهما حاليًا في شيء منطقي حقًا :)

شيء معقد مثل مترجم TS تمت كتابته بدون حراس ، وهذا شيء يجب التفكير فيه.

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

jbondc راجع تعليقي أعلاه بقائمة المشكلات في برنامج التحويل البرمجي TypeScript الناتجة عن القيم الخالية وغير المحددة (باستثناء أول واحد في القائمة)

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

واحدة من المشكلات الكبيرة التي تم ذكرها لجعل الأنواع غير قابلة للإلغاء هي ما يجب فعله حيال كود TypeScript الموجود. هناك مشكلتان هنا: 1) جعل الكود الحالي يعمل مع مترجم أحدث يدعم الأنواع غير القابلة للإلغاء و 2) التشغيل البيني مع الكود الذي لم يتم تحديثه - تجنب مستنقع Python 2/3

يجب أن يكون من الممكن على الرغم من توفير أداة تقوم بإعادة كتابة تلقائية لرمز TS الحالي لجعله يبني وفي هذه الحالة المحدودة ، شيء يعمل بشكل أفضل بكثير مما فعل 2to3 مع Python.

ربما تبدو الهجرة التدريجية كما يلي:

  1. تقديم دعم لبناء جملة "؟ T" للإشارة إلى نوع فارغ محتمل. في البداية ، ليس لهذا أي تأثير على فحص النوع ، فهو موجود فقط للتوثيق.
  2. قم بتوفير أداة تقوم تلقائيًا بإعادة كتابة التعليمات البرمجية الحالية لاستخدامها "؟ T". التطبيق الأكثر غباءًا من شأنه تحويل جميع استخدامات "T" إلى "؟ T". من شأن التطبيق الأكثر ذكاءً التحقق مما إذا كانت الشفرة التي استخدمت النوع افترضت ضمنيًا أنها غير فارغة وتستخدم حرف "T" في هذه الحالة. بالنسبة إلى الكود الذي يحاول تعيين قيمة من النوع "؟ T" إلى معلمة أو خاصية تتوقع "T" ، فقد يؤدي ذلك إلى التفاف القيمة في عنصر نائب يؤكد (في وقت الترجمة) أن القيمة ليست خالية - على سبيل المثال let foo: T = expect(possiblyNullFoo) أو let foo:T = possiblyNullFoo!
  3. قم بتطبيق الأداة على جميع تعريفات الأنواع المتوفرة وتلك الموجودة في مستودع نوع DefinitelyTyped
  4. قم بإدخال تحذير المترجم عند محاولة تعيين "؟ T" إلى الفتحة التي تتوقع "T".
  5. دع التحذيرات تخبز في البرية وتحقق من أن النقل يمكن التحكم فيه وشاهد كيف يتم تلقي التغيير
  6. اجعل تعيين "؟ T" في الفتحة التي تتوقع خطأ "T" ، في إصدار رئيسي جديد من المترجم.

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

في التعليق السابق ، أفترض أنه لن يكون هناك وضع لاختيار قابلية إلغاء حرف "T" العادي لتجنب تقسيم اللغة وأن الخطوات 1-3 ستكون مفيدة بمفردها حتى لو لم تثبت الخطوة 6 أنها عملية.

أفكاري العشوائية:

  1. قد يكون الجزء expect() معقدًا ، ويجب أن يكون مدروسًا جيدًا. لا توجد تعيينات فقط ، ولكن توجد واجهات أو عوامل عامة أو أنواع معلمات ...
  2. I _think_ TS ليس لديه أي تحذير ، فقط أخطاء. إذا كنت على حق ، فلست متأكدًا من أنهم سيقدمون تحذيرات لهذه الميزة فقط.
  3. حتى إذا قام العديد من الأشخاص بترقية التعليمات البرمجية الخاصة بهم ، فقد تكون المؤسسات بطيئة جدًا في القيام بذلك ، أو قد لا ترغب في القيام بذلك أبدًا في حالة قواعد التعليمات البرمجية الكبيرة الموجودة. هذا يجعل هذا الإصدار الكبير تغييرًا هائلاً ، وهو أمر لا يريده فريق TS.

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

@ jods4 - بالنسبة expect() ، أعتقد أنه أينما كان لديك ?T يرى المترجم T | undefined حتى تتمكن من إعادة استخدام القواعد للتعامل مع أنواع الاتحاد بقدر ما المستطاع.

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

شيء آخر ذي صلة بهذه المناقشة - https://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdf يناقش التحقيقات التي أجراها فريق Chrome في استخدام معلومات النوع (المحددة عبر TypeScript-style التعليقات التوضيحية) في المترجم لتحسين JS. هناك سؤال مفتوح حول الإلغاء - فهم يرغبون في الأنواع غير القابلة للإلغاء ولكنهم غير متأكدين من الجدوى أيضًا.

فقط سوف تتناغم مرة أخرى هنا. أنا مندهش من أننا لم نحل هذا الأمر حقًا.

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

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

لماذا لا يمكنك الإقلاع عن استخدام الحرف الفارغ وحراسة جميع الأماكن التي بها
يمكن أن تتسرب القيم الخالية إلى التعليمات البرمجية الخاصة بك؟ بهذه الطريقة ستكون في مأمن من العدم
الاستثناء المرجعي دون الحاجة إلى إجراء عمليات تحقق من القيمة الفارغة في كل مكان.
في 20 فبراير 2015 1:41 ظهرًا ، كتب "electricessence" [email protected] :

فقط سوف تتناغم مرة أخرى هنا. أنا مندهش من أننا لم نحل المشكلة حقًا
هذا.

أقول هناك قيمة مضافة هنا. واحد لا ينطبق إلا في الترجمة
زمن.
بدلاً من الاضطرار إلى كتابة المزيد من التعليمات البرمجية لإلغاء التحقق من القيمة ، تكون قادرًا على ذلك
إعلان قيمة على أنها غير قابلة للصفقة يمكن أن يوفر الوقت والتشفير. إذا وصفت أ
اكتب كـ "non-nullable" ثم يجب تهيئته وربما تأكيده
على أنها غير خالية قبل أن يتم تمريرها إلى دالة أخرى تتوقعها
غير فارغ.

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

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204
.

تعتبر عمليات التحقق من القيمة الفارغة أسرع بشكل ملحوظ من عمليات التحقق غير المحددة.
في 21/02/2015 11:54 صباحًا ، كتب "Aleksey Bykov" [email protected] :

لماذا لا يمكنك الإقلاع عن استخدام الحرف الفارغ وحراسة جميع الأماكن التي بها
يمكن أن تتسرب القيم الخالية إلى التعليمات البرمجية الخاصة بك؟ بهذه الطريقة ستكون في مأمن من العدم
الاستثناء المرجعي دون الحاجة إلى إجراء عمليات تحقق من القيمة الفارغة في كل مكان.
في 20 شباط (فبراير) 2015 ، الساعة 1:41 مساءً ، تلقي إشعارات "electricessence "@github.com
كتب:

فقط سوف تتناغم مرة أخرى هنا. أنا مندهش من أننا لم نحل المشكلة حقًا
هذا.

أقول هناك قيمة مضافة هنا. واحد لا ينطبق إلا في الترجمة
زمن.
بدلاً من الاضطرار إلى كتابة المزيد من التعليمات البرمجية لإلغاء التحقق من القيمة ، تكون قادرًا على ذلك
إعلان قيمة على أنها غير قابلة للصفقة يمكن أن يوفر الوقت والتشفير. إذا وصفت أ
اكتب كـ "non-nullable" ثم يجب تهيئته وربما
أكد
على أنها غير خالية قبل أن يتم تمريرها إلى دالة أخرى تتوقعها
غير فارغ.

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

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75295204>
.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198
.

نقطتي أن هذه الشيكات غير ضرورية (إذا تم حظر الكلمة المفتاحية الفارغة
وجميع الأماكن الأخرى التي يمكن أن تأتي فيها محمية).

لماذا ا؟ 1. يعمل الكود بدون تحقق على الإطلاق بشكل أسرع من الكود
مع الشيكات للقيم الخالية. 2. أنه يحتوي على عدد أقل من if وبالتالي أكثر قابلية للقراءة
وقابلة للصيانة.
في 20 فبراير 2015 ، الساعة 7:57 مساءً ، كتب "Griffork" [email protected] :

تعتبر عمليات التحقق من القيمة الفارغة أسرع بشكل ملحوظ من عمليات التحقق غير المحددة.
في 21/02/2015 11:54 صباحًا ، كتب "Aleksey Bykov" [email protected] :

لماذا لا يمكنك الإقلاع عن استخدام الحرف الفارغ وحراسة جميع الأماكن التي بها
يمكن أن تتسرب القيم الخالية إلى التعليمات البرمجية الخاصة بك؟ بهذه الطريقة ستكون في مأمن من العدم
الاستثناء المرجعي دون الحاجة إلى إجراء عمليات تحقق من القيمة الفارغة في كل مكان.
في 20 شباط (فبراير) 2015 ، الساعة 1:41 مساءً ، تلقي إشعارات "electricessence "@github.com
كتب:

فقط سوف تتناغم مرة أخرى هنا. أنا مندهش من أننا لم نحل المشكلة حقًا
هذا.

أقول هناك قيمة مضافة هنا. واحد لا ينطبق إلا في الترجمة
زمن.
بدلاً من الاضطرار إلى كتابة المزيد من التعليمات البرمجية لإلغاء التحقق من القيمة ، تكون قادرًا
ل
إعلان قيمة على أنها غير قابلة للصفقة يمكن أن يوفر الوقت والتشفير. اذا انا
وصف أ
اكتب كـ "non-nullable" ثم يجب تهيئته وربما
أكد
على أنها غير خالية قبل أن يتم تمريرها إلى دالة أخرى تتوقعها
غير فارغ.

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

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

.

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346198>
.

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346390
.

أنت تفتقد النقطة ، نستخدم القيم الخالية لتجنب عمليات التحقق غير المحددة.
ونظرًا لأن تطبيقنا لا يحتوي على حالة واحدة متسقة ، فإننا نمتلك و
غالبًا ما يجب أن تكون الأشياء خالية / غير محددة بدلاً من أن تكون كائنًا
تمثل ذلك.
في 21/02/2015 12:20 ظهرًا ، كتب "Aleksey Bykov" [email protected] :

نقطتي أن هذه الشيكات غير ضرورية (إذا تم حظر الكلمة المفتاحية الفارغة
وجميع الأماكن الأخرى التي يمكن أن تأتي فيها محمية).

لماذا ا؟ 1. يعمل الكود بدون تحقق على الإطلاق بشكل أسرع من الكود
مع الشيكات للقيم الخالية. 2. أنه يحتوي على عدد أقل من if وبالتالي أكثر قابلية للقراءة
وقابلة للصيانة.
في 20 فبراير 2015 ، الساعة 7:57 مساءً ، كتب "Griffork" [email protected] :

تعتبر عمليات التحقق من القيمة الفارغة أسرع بشكل ملحوظ من عمليات التحقق غير المحددة.
بتاريخ 21/02/2015 11:54 صباحًا ، تلقيت "أليكسي بيكوف" إخطارات github.com
كتب:

لماذا لا يمكنك الإقلاع عن استخدام الحرف الفارغ وحراسة جميع الأماكن
أين
يمكن أن تتسرب القيم الخالية إلى التعليمات البرمجية الخاصة بك؟ بهذه الطريقة ستكون في مأمن من العدم
الاستثناء المرجعي دون الحاجة إلى إجراء عمليات تحقق من القيمة الفارغة في كل مكان.
في 20 شباط (فبراير) 2015 ، الساعة 1:41 مساءً ، تلقي إشعارات "electricessence "@github.com
كتب:

فقط سوف تتناغم مرة أخرى هنا. أنا مندهش من أننا لم نفعل ذلك حقًا
تم الحل
هذا.

أقول هناك قيمة مضافة هنا. واحد لا ينطبق إلا في
تجميع
زمن.
بدلاً من الاضطرار إلى كتابة المزيد من التعليمات البرمجية لإلغاء التحقق من القيمة ، يجري
قادر
ل
إعلان قيمة على أنها غير قابلة للصفقة يمكن أن يوفر الوقت والتشفير. اذا انا
وصف أ
اكتب كـ "non-nullable" ثم يجب تهيئته وربما
أكد
على أنها غير خالية قبل أن يتم تمريرها إلى دالة أخرى تتوقعها
غير فارغ.

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

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<

https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75295204

.

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75346198

.

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-75346390>
.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75347832
.

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

class Nothing { public 'i am nothing': Nothing; }
class Something { 
    constructor(
    public name: string,
    public value: number
   ) { }
}
var nothing = new Nothing();
var whoami = Math.random() > 0.5 ? new Something('meh', 66) : nothing;

if (whoami instanceof Nothing) {
    console.log('i am a null killer');
} else if (whoami instanceof Something) {
    console.log(whoami.name + ': ' + whoami.value);
}

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

كم ذلك رائع؟

هل تعتقد أن مثيل الشيك أسرع من فحص فارغ ، لملف
التطبيق الذي يجب أن يفعل المئات من هؤلاء في الثانية؟
اسمحوا لي أن أضعها على هذا النحو: كان علينا إنشاء أسماء مستعارة للوظائف لأن
من الأسرع عدم استخدام "." الملحقات.
في 21/02/2015 12:58 ظهرًا ، كتب "Aleksey Bykov" [email protected] :

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

فئة لا شيء {عامة 'أنا لا شيء': لا شيء ؛ }
فئة شيء ما {
البناء(
الاسم العام: سلسلة ،
القيمة العامة: رقم
) {}
}
var لا شيء = لا شيء جديد () ؛
var whoami = Math.random ()> 0.5؟ شيء جديد ('meh' ، 66): لا شيء ؛

إذا (whoami exampleof لا شيء) {
// whoami لا شيء
console.log ('أنا قاتل باطل') ؛
} else if (whoami exampleof Something) {
// whoami هو مثال على شيء ما
console.log (whoami.name + ':' + whoami.value) ؛
}

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

كم ذلك رائع؟

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75349967
.

@ aleksey-bykov لاحظ أنني تناولت هذه النقطة بالفعل: لم يتم حل الحاجة إلى نموذج تعريفات النوع للمكتبات الحالية التي تستخدم بالفعل قيمًا فارغة. في هذه الحالة ، لا يمكنك "إنهاء استخدام" القيم الخالية فقط.

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

Griffork ، لا أعتقد أنني
http://jsperf.com/nullable-vs-null-vs-undefined-vs-instanceof
لكن ما أعتقده هو أنك بحاجة إلى إيجاد توازن بين الأمان والأداء. ثم تحتاج إلى قياس أدائك بعناية قبل محاولة تحسينه. من المحتمل أنك تقوم بإصلاح شيء لم يتم كسره وقد تشكل عمليات التحقق الفارغة التي تشعر بالقلق الشديد بشأنها أقل من 2٪ من الأداء الإجمالي ، إذا كان الأمر كذلك ، إذا جعلته يتضاعف بشكل أسرع ، فستكسب 1٪ فقط.

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

تتكون قاعدة الأكواد الخاصة بنا من أكثر من 800 ملف TypeScript وأكثر من 120000 سطر من التعليمات البرمجية
ولم نحتاج أبدًا إلى قيم خالية أو غير محددة عندما يتعلق الأمر بنمذجة الأعمال
كيانات المجال. وعلى الرغم من أنه كان علينا استخدام القيم الخالية لمعالجة DOM ،
يتم عزل كل هذه الأماكن بعناية ، بحيث لا يكون هناك أي طريقة لتسرب القيم الخالية
في. أنا لا أشتري حجتك القائلة بأن القيم الخالية قد تكون مطلوبة ، فهناك
إنتاج لغات جاهزة بدون قيم خالية على الإطلاق (هاسكل) أو خالية
محظور (F #).
في 22 فبراير 2015 ، الساعة 9:31 صباحًا ، كتب "Jon" [email protected] :

@ aleksey-bykov https://github.com/aleksey-bykov من عملي
منظور ، لا يمكنك تجاهل فارغة. إليك بعض المعلومات الأساسية عن سبب قيامك بذلك
بحاجة إلى نوع nullable على أي حال:
https://www.google.com/patents/US7627594؟ei=P4XoVPCaEIzjsATm4YGoBQ&ved=0CFsQ6AEwCTge

قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -75437927
.

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

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

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

إذا قرأت أعلاه أيضًا ، فإن الاقتراح غير القابل للإلغاء يغطي أيضًا غير محدد.

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

مرة أخرى ، ربما لا داعي للقلق ، إلا إذا كنت (مثلنا) تقوم بإجراء 10000 عملية تحقق في أقل من 10 مللي ثانية.

(ونعم ، لقد تغيرنا بالفعل بسبب تتبع أداء تطبيقنا وإيجاد عنق الزجاجة)

أود أن أثير بعض النقاش حول هذا. بالأمس فقط في Build video رأيت أدوات التحليل مطبقة في C #. فتحت إصدارًا رقم 3003 لتطبيق مشابه لـ TypeScript.

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

في الحقيقة أرى أن المحلل في TypeScript سيكون رائعًا لأشياء أخرى أيضًا. توجد مكتبات غريبة حقًا ، أكثر من مكتبات C #.

لأي شخص يتابع هذه المشكلة: إنني أحتفظ بمكتبة تسمى

نرحب بالتعليقات!

لقد قرأت للتو من خلال الموضوع بأكمله.

أعتقد أن الاقتراح يجب أن يستخدم مصطلحات non-voidable و voidable ، لأن void في مواصفات TS هو نوع لقيمة null و undefined . (أفترض أن الاقتراح يغطي أيضًا قيمة غير محددة: ابتسامة:)

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

الرجاء إضافة هذا! وسيكون من الأفضل إذا أصبحت هذه ميزة يمكنني تمرير علامة لجعلها الميزة الافتراضية. سيوفر الكثير من تصحيح الأخطاء للكثير من الناس.

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

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

أرى أنه عيب كبير في التفكير في ADTs كحل للمشكلة ...

  1. احصل على ADT متاحًا من خلال نمط تصميم (بسبب عدم وجود ملفات
    دعم من TypeScript)
  2. منع الكلمة الرئيسية null
  3. حماية جميع الأماكن التي يمكن أن تتسرب فيها القيم الخالية إلى التعليمات البرمجية الخاصة بك من الخارج
  4. استمتع بمشكلة null ذهبت إلى الأبد

في مشروعنا ، تمكنا من تنفيذ الخطوات 1 و 2 و 3. خمن ما نقوم به
الآن.

@ aleksey-bykov

كيف تعمل (2) بـ undefined ؟ يمكن أن تنشأ في مجموعة متنوعة من المواقف التي لا تتضمن الكلمة الأساسية undefined : أعضاء فئة غير مهيئين ، حقول اختيارية ، عبارات إرجاع مفقودة في الفروع الشرطية ...

قارن الكتابة المطبوعة بالنمط الانسيابي . على الأقل يتعامل التدفق مع معظم الحالات.

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

يمكن أن تنشأ في مجموعة متنوعة من المواقف التي لا تتضمن كلمة رئيسية غير محددة ...

  • أعضاء الفصل غير المهيئين - الفئات ممنوعة
  • الحقول الاختيارية - الحقول الاختيارية / المعلمات ممنوعة
  • عبارات الإرجاع المفقودة - قاعدة tslint مخصصة تتأكد من عودة جميع مسارات التنفيذ

أفضل حالا باستخدام purescript

  • أتمنى لكن الكود الذي يولده يتعلق بالأداء الأقل

@ aleksey-bykov من المضحك أنك ذكرت الأداء ، لأن استبدال الأنواع الفارغة بـ ADTs يفرض زيادة كبيرة في وحدة المعالجة المركزية والذاكرة مقارنة بفرض عمليات التحقق الفارغة (مثل نوع التدفق). الشيء نفسه ينطبق على عدم استخدام الفئات (يمكن أن يكون مكلفًا للغاية إذا قمت بإنشاء الكثير من الكائنات مع الكثير من الأساليب عليها)

لقد ذكرت التخلي عن الحقول الاختيارية لكنها مستخدمة من قبل العديد من مكتبات JS. ماذا فعلت للتعامل مع تلك المكتبات؟

لقد ذكرت أيضًا عدم استخدام الفصول الدراسية. أفترض أيضًا أنك تتجنب المكتبات التي تعتمد (أو ستعتمد) على الفئات (مثل React)؟

على أي حال ، لا أرى سببًا للتخلي عن العديد من الميزات عندما يكون من المحتمل جدًا تنفيذ حل معقول تمامًا (يناسب اللغة الأساسية غير المكتوبة).

من المضحك أنك تعتقد أن الغرض الوحيد من ADT's هو تمثيل قيمة مفقودة كان من الممكن ترميزها بواسطة null

أفضل القول إن حل المشكلة "الفارغة" (والصفوف أيضًا) هو نتيجة ثانوية لكل الأشياء الجيدة التي تجلبها لنا ADT

معالجة فضولك ، للحصول على أجزاء مهمة للأداء مثل الحلقات الضيقة وهياكل البيانات الكبيرة ، نستخدم ما يسمى Nullable<a> التجريد عن طريق خداع TypeScript للاعتقاد بأنها تتعامل مع قيمة مغلفة ، ولكن في الواقع (في وقت التشغيل) هذه القيمة هي فقط بدائية مسموح بها أخذ الأصفار ، هناك مجموعة خاصة من العمليات على Nullable تمنع تسرب هذا العدم

اسمحوا لي أن أعرف إذا كنت تريد معرفة التفاصيل

interface Nullable<a> {
    'a nullable': Nullable<a>;
    'uses a': a;
}
/*  the following `toNullable` function is just for illustration, we don't use it in our code,
    because there are no values capable of holding naked null roaming around,
    instead we just alter the definition of all unsafe external interfaces:
    // before
    interface Array<T> {
       find(callback: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T;
    }
    // after
    interface Array<a> {
       find(callback: (value: a, index: number, array: a[]) => boolean, thisArg: any): Nullable<a>;
    }
*/
function toNullable<a>(value: a) : Nullable<a> {
    return <any>value;
}
function toValueOrDefault<a>(value: Nullable<a>, defaultValue: a)  : a {
    return value != null ? <any>value : defaultValue;
}

لماذا نستخدم بنية النوع العودي؟ يجب أن يكون 'a nullable': a كافيًا ، أليس كذلك؟

أفترض أن لديك أيضًا callFunctionWithNullableResult<T,U>(f: (T) => U, arg:T):Nullable<U> (بما في ذلك جميع الأحمال الزائدة الأخرى للوظائف ذات المجالات المختلفة) للتعامل مع جميع الوظائف الخارجية التي تُرجع القيم الخالية؟

وإذا كانت الكلمة المفتاحية الفارغة ممنوعة بواسطة لينتر ، فكيف يمكنك تخزين " Nothing " في ملف nullable؟ هل لديك حالة خاصة toNullable(null) ؟

أعرف ما هي أنواع البيانات الجبرية. هل يمكنك أن تعطيني مثالاً (بخلاف فحوصات الشمولية والكلمة) حيث يمكن لـ ADTs (أو بالأحرى أنواع المجموع) + مطابقة النمط أن تفعل شيئًا لا تستطيع اتحادات الكتابة المطبوعة + حراس النوع المحدد من قبل المستخدم القيام به؟

لماذا نستخدم بنية النوع العودي؟ "لاغية": يجب أن تكون كافية ، أليس كذلك؟

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

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

أفترض أن لديك أيضًا نتيجة callFunctionWithNullable(f: T -> U ، arg: T) => Nullable

لا ، كما قلت ، قمنا بتعديل ملفات التعريف (* .d.ts) عن طريق استبدال أي شيء لدينا دليل يمكن أن يكون فارغًا في الكود الخارجي بـ Nullable "مجمّع" ، انظر المثال في التعليق في رسالتي السابقة

كيف تحصل على "لا شيء" مخزنة في nullable

نحن لا نخزن القيم الخالية في العناصر الفارغة ، تأتي العناصر الفارغة من الخادم (الذي لدينا سيطرة محدودة عليه) أو من واجهة برمجة التطبيقات الخارجية ، بمجرد أن تكون في الكود الخاص بنا ، يتم تغليفها في Nullable ، لكن الكود الخاص بنا غير قادر على الإنتاج لاغية حسب الرغبة ، يمكننا فقط تحويل nullable معطى إلى nullable آخر ، ولكن ليس لخلق واحد من فراغ

لا يمكن لاتحادات الطباعة المطبوعة + حراس النوع المحدد من قبل المستخدم القيام بذلك

ها! أنا لست من المعجبين بأنواع النقابات على الإطلاق ويمكنني التحدث لساعات عما يجعلها فكرة سيئة

بالعودة إلى سؤالك ، أعطني ما يعادل Either a b قابل لإعادة الاستخدام باستخدام النقابات واكتب الحراس
(تلميح: https://github.com/Microsoft/TypeScript/issues/2264)

الأنواع الموجودة في المثال الخاص بك ليست أكثر رمزية لمجرد أنها متكررة - فهي تظل "اسمية زائفة".

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

type MyType = A | B

ثم استخدم حراس النوع لـ A و B ... كمكافأة ، يعمل هذا مع كل مكتبة JS موجودة أيضًا: على سبيل المثال ، الطريقة الاصطلاحية للتحقق من Thenable إذا كان هناك هي طريقة then مرتبطة بالكائن. هل سيعمل حارس النوع من أجل ذلك؟ بكل تأكيد.

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

enum GottaBeNonimalBrand {}
interface GottaBeNonimal {
     branded: GottaBeNonimalBrand;
}
function toGottaBeNominal() : GottaBeNominal {
   return <any> {};
}

انتظر لحظة! انت غششت ! الطريقة القديمة الجيدة لا تزال تعمل مثل السحر.

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

هذا لطيف ما تقوله ، كيف حاله Either رغم ذلك؟ أن أصيلة Either هو عام، وهناك نحو 30 وظائف الراحة القياسية حوله، الآن أنت تقول أنا بحاجة للذهاب حلها (الاسمية) ويكون ما لا يقل عن 30 × number_of_combinations_of_any_2_types_in_my_app تلك الوظائف لجميع الأزواج الممكنة أنا قد يواجه .. ينجز؟

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

C # vnext العصف الذهني (باختصار)

بشكل أساسي ، في C # لديك أنواع قيم غير خالية ، مثل int . ثم في C # 2.0 حصلنا على أنواع القيم الفارغة ، مثل int? . لطالما كانت المراجع فارغة ، لكنهم يفكرون في تغيير ذلك وتطبيق نفس الصيغة: string ليس فارغًا أبدًا ، string? قد يكون كذلك.

بالطبع ، المشكلة الكبرى هي أن هذا يغير معنى string ويكسر الكود. حيث أنه من المقبول تعيين / إرجاع null إلى string ، فهذا خطأ الآن. يفكر فريق C # في "تعطيل" هذه الأخطاء ما لم تقم بالاشتراك. دائمًا ما يكون التنقيط على string? خطأ ، لأن بناء الجملة جديد ولم يكن مسموحًا به من قبل (ومن ثم فهو ليس تغييرًا فاصلًا).
المشكلة الأخرى مع المكتبات الأخرى ، وما إلى ذلك. لمعالجة ذلك ، يريدون عمل علامة التقيد لكل ملف أو تجميع. لذا فإن الشفرة الخارجية التي تختار نظام النوع الأكثر صرامة تحصل على الفوائد ، وتستمر الكود والمكتبات القديمة في العمل.

كيف يمكن أن ينتقل ذلك إلى TS؟

  1. نقدم علامة اختيار أنواع غير خالية. يمكن أن يكون عالميًا (يتم تمريره إلى المترجم) أو لكل ملف. يجب أن يكون دائمًا لكل ملف مقابل .d.ts .
  2. الأنواع الأساسية غير قابلة للإلغاء ، على سبيل المثال number . هناك نوع جديد من null .
  3. number? هو اختصار لـ number | null .
  4. تقدم الميزة أخطاء جديدة لأنواع nullable ، مثل (x: string?) => x.length بدون نوع guard.
  5. تقدم الميزة أخطاء جديدة للأنواع غير القابلة للإلغاء ، مثل تعيين let x: string = null;
  6. عند معالجة نوع غير فارغ تم الإعلان عنه خارج نطاق الاشتراك ، يتم تجاهل الأخطاء المبلغ عنها ضمن 5. _ هذا يختلف قليلاً عن قول string لا يزال يُعتبر string? ._

ما فائدة هذا؟

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

عندما أستخدم رمزًا قديمًا ، يمكنني الاشتراك في الملفات الجديدة. يمكنني التصريح عن string? أي حال والحصول على أخطاء عند الوصول إلى الأعضاء دون التحقق من القيمة الفارغة المناسبة. أحصل أيضًا على مزايا وعمليات فحص صارمة للمكتبات التي تم تحديث تعريفاتها. بمجرد اختيار .d.ts ، يصبح تمرير null إلى دالة معرفة على أنها length(x: string): number خطأ.
_ (ملاحظة: تمرير string? خطأ أيضًا ، على الرغم من أن تمرير string من رمز إلغاء الاشتراك ليس كذلك.) _

طالما لم أقم بالاشتراك ، فلا يوجد تغيير جذري.

ما رأيك؟

بالطبع ، هناك الكثير مما يجب مراعاته فيما يتعلق بكيفية عمل الميزة في الممارسة العملية. ولكن هل نظام الاشتراك هذا أمر قد يفكر فيه فريق TS؟

إنه لأمر مدهش كيف يريد الناس حصانًا أسرع بدلاً من سيارة ..

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

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

@ aleksey-bykov وبالطبع للحصول على قيود أفضل على الكتابة ، تقوم بتنفيذ أنواع ذات أنواع أعلى ، وللحصول على مثيلات مشتقة ، تقوم بتطبيق فئات الطباعة وما إلى ذلك. كيف يتناسب ذلك مع هدف تصميم "التوافق مع ES6 +" في TypeScript؟

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

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

ما هي مشكلتك في الأداء مع PureScript؟ هل هي عمليات الإغلاق التي ينتجها انتشار الكاري؟

@ jods4 يعني أن جميع ملفات .d.ts الموجودة حاليًا ستحتاج إلى تحديث بعلامة "خارج العمل" ، أو أن الإعداد على مستوى المشروع لا يؤثر على .d.ts (و .d.ts بدون علم يُفترض دائمًا أنه الطريقة "القديمة".
ما مدى فهم هذا (هل سيسبب مشاكل للأشخاص الذين يعملون في مشاريع متعددة أو التباس عند قراءة ملفات .d.ts)؟
كيف ستبدو الواجهة بين كود (مكتبة) الجديد والقديم. هل سيتم تناثرها بمجموعة من (في بعض الحالات) عمليات الفحص والاطلاع غير الضرورية (ربما ليس شيئًا سيئًا ولكنه قد يؤدي إلى إبعاد المبتدئين عن اللغة)؟
في الغالب (الآن) أحب فكرة الأنواع غير القابلة للإلغاء ، والاقتراح ! هو الطريقة الوحيدة التي أرى بها الأنواع غير القابلة للإلغاء تعمل (تجاهل adts).

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

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

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

هناك متغير / خاصية غير محددة وهي من النوع البدائي غير معرف. null هو حرفي (والذي يحدث أيضًا أن يكون من النوع البدائي null

@ kitsonk هذه حجة كبيرة لهذه القضية ؛ null ليس number ، إنه ليس أكثر من Person من Car . إنه نوعه الخاص وهذه المشكلة تتعلق بالسماح للطباعة المطبوعة بالتمييز.

kitsonk أعتقد أن أحد الأخطاء الأكثر شيوعًا هو أخطاء مرجعية / وصول فارغة غير محددة. إن امتلاك القدرة على تحديد خاصية أو متغير بحيث لا يكون فارغًا / غير معرف سيجعل كود البرنامج أكثر صوتًا. ودع أيضًا أدوات مثل LS للقبض على الأخطاء مثل هذا https://github.com/Microsoft/TypeScript/issues/3692.

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

interface Mixed {
    optional?: string;
    notOptional: string;
    nonNullable!: string;
}

function mixed(notOptional: string, notNullable!: string, optional?: string): void {}

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

نظرًا لوجود وسيطة "اختيارية" بالفعل في TypeScript ، فلماذا لا يعمل ما يلي كاقتراح؟

فقط لتوضيح الاختيارات في الوقت الحالي في TS هي اختيارية فقط أثناء التهيئة. Null و undefined لجميع الأنواع.

هذه بعض الحالات غير البديهية من الاختيارات

interface A {
   a?: string;
}
let a: A = {} // ok as expected

interface B {
   b: string;
}
let b1: B = { b: undefined } //ok, but unintuitive
let b2: B = { b: null } // ok, but unintuitive
let b3: B = {} // error as expected

أو لمجرد إبقائها بسيطة optional في TS يعني غير قابل للتهيئة. لكن هذا الاقتراح ينص على أنه لا يمكن تعيين undefined و null إلى نوع non-voidable (non-null، non-undefined).

يتم وضع ? بواسطة اسم الخاصية / الوسيطة لأنها تشير إلى _وجود_ لهذا الاسم أم لا. tinganho أوضح ذلك جيدًا. توسيع مثاله:

function foo(arg: string, another?: string) {
  return arguments.length;
}

var a: A = {};
a.hasOwnProperty('a') // false
foo('yo') // 1, because the second argument is _non-existent_.
foo(null) // this is also ok, but unintuitive

لا علاقة لـ ! غير الفارغة بـ _وجود_ لاسم. إنه مرتبط بالنوع ، ولهذا السبب يجب وضعه حسب النوع. النوع غير قابل للإلغاء.

interface C {
  c: !string;
}

let c1: C = { }; // error, as expected
let c2: C = { c: null }; // error, finally!
let c3: C = { c: 'str' }; // ok! :)

function bar(arg: !string) {
  return arg.length;
}

bar(null) // type error
bar('foo') // 3

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

kitsonk المشكلة التي يحلها هذا هي تقليل الأخطاء لأن الناس يفترضون أن شيئًا ما ليس فارغًا عندما يكون كذلك ، أو يمررون القيم الخالية إلى وظائف لا تدعمه. إذا كنت تعمل في مشاريع كبيرة مع العديد من الأشخاص ، فقد تستهلك وظائف لم تكتبها. أو قد تستهلك مكتبات الطرف الثالث التي لا تعرفها جيدًا. عندما تفعل: let x = array.sum(x => x.value) ، هل يمكنك افتراض أن x ليس فارغًا أبدًا؟ ربما تكون 0 إذا كانت المصفوفة فارغة؟ كيف علمت بذلك؟ هل يسمح لمجموعتك بامتلاك x.value === null ؟ هل يدعم ذلك وظيفة sum() أم أنه سيكون خطأ؟
باستخدام هذه الميزة ، يتم التحقق من هذه الحالات بشكل ثابت وتمرير قيمة فارغة محتملة إلى وظيفة لا تدعمها أو تستهلك قيمة فارغة بدون فحص يتم الإبلاغ عنها كأخطاء.
في الأساس ، في برنامج مكتوب بالكامل ، لا يمكن أن يكون هناك "NullReferenceException" بعد الآن (باستثناء حالات الركن المتعلقة بـ undefined ، للأسف).

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

النوع _void_ لا يحتوي على أية قيم ولكن قيمتي null و undefined يمكن تخصيصهما له

هنا ملخص جيد: https://github.com/Microsoft/TypeScript/issues/185#issuecomment -71942237

jbondc أعتقد أنني راجعت المواصفات مؤخرًا. void مصطلح شامل لـ null و undefined . لا أعرف عن void 0 ، لكني أعتقد أن void لديه أيضًا تحت مظلته.

يستدل من any . ولكن لا يزال من الممكن null و undefined لجميع الأنواع.

يُرجع void 0 دائمًا undefined أيضًا http://stackoverflow.com/a/7452352/449132.

jbondc إذا كان السؤال حول نظام نوع مدرك

function returnsNull() {
  return null;
}

يجب استنتاج النوع null .

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

في المرة الأخيرة التي شاركت فيها في هذه المناقشة ، انتهت بـ: "لن نقوم بتغييرات عاجلة ولا نريد تغيير معنى كود المصدر بناءً على العلامات / الخيارات المحيطة". الفكرة التي أصفها أعلاه ليست تغييرًا جذريًا وهي جيدة نسبيًا فيما يتعلق بتفسير المصدر ، على الرغم من أنها ليست 100٪. تلك المنطقة الرمادية هي سبب سؤالي عن رأي فريق TS.

@ jods4 أفضل القيمة المستنتجة any ، والتي ستحتفظ بالصيغة الحالية. وستكون استراتيجية التقيد هي الأفضل. [1]

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

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

[1] تقديم علامة لجعل هذا الخيار الافتراضي لن يعمل بشكل جيد في المقام الأول لأسباب عملية. ما عليك سوى إلقاء نظرة على المشكلات التي واجهها الأشخاص بـ --noImplicitAny ، والتي أدت إلى بعض مخاطر الهجرة ، والكثير من المشكلات العملية في اختبار وجود الممتلكات التي أدت إلى --suppressImplicitAnyIndexErrors ، والعديد من التعريفات المعطلة بالتأكيد.

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

أفضل القيمة المستنتجة any ، والتي ستحتفظ بالصيغة الحالية. وستكون استراتيجية التقيد هي الأفضل.

لقد قمت بتعديل إجابتي هنا. بالتفكير أكثر في هذا الأمر ، لا أرى أي حالة مفيدة حيث يمكنك ضمنيًا استنتاج نوع null فقط. مثل: () => null أو let x = null أو function y() { return null } .

لذلك أوافق على أن الاستمرار في استنتاج any أمر جيد على الأرجح.

  1. إنه متوافق مع الإصدارات السابقة.
  2. كل هذه الحالات هي أخطاء أقل من noImplicitAny على أي حال.
  3. إذا كانت لديك حالة غريبة حيث تريد القيام بذلك وكان ذلك مفيدًا لك ، فيمكنك التصريح بوضوح عن النوع: (): null => null أو let x: null = null أو function y(): null { return null } .

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

إن القول بأن string يعني في الواقع string! | null قد يكون خيارًا آخر. تجدر الإشارة إلى أنك إذا قرأت جميع المناقشات أعلاه ، فقد تم اقتراح هذا في البداية لأنه كان هناك أمل في أن يكون أكثر توافقًا مع الكود الحالي (وليس تغيير المعنى الحالي لـ string ). ولكن في الواقع كان الاستنتاج أنه بالرغم من ذلك ، سيتم كسر كميات هائلة من التعليمات البرمجية على أي حال.

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

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

أعتقد أن الأمور أصبحت محيرة في هذه المرحلة. بالنسبة لي ، تبدو هذه حجة جيدة لاستخدام string? كل مكان. لا أرغب في رؤية الخلط بين number? و string! ، فقد يصبح هذا مربكًا بسرعة كبيرة جدًا.

[1] تقديم علامة لجعل هذا الخيار الافتراضي لن يعمل بشكل جيد في المقام الأول لأسباب عملية.

نعم ، لن تعمل بشكل جيد مع قاعدة رمز موجودة بدون تغييرات. ولكن:

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

@ jods4

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

لم أقصد ذلك بهذا المعنى. قصدت أن هناك حالة استخدام أقل للأرقام الفارغة مقارنة بالسلاسل الفارغة. أنا لست مرتبطًا بهذا الاقتراح ، على أي حال (لن أهتم بأيٍّ من الحالتين).

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

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

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

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

ربما أكون مخطئا. لم أستخدمها مطلقًا ولكن عند النظر في حالات الاختبار ، أرى الكثير من التعليقات تبدو وكأنها // <strong i="9">@module</strong> amd . لطالما افترضت أنها طريقة لتحديد الخيارات من داخل ملف ts. لم أحاول أبدًا القيام بذلك ، لذلك ربما أكون مخطئًا تمامًا! ابحث عن مثال هنا:
https://github.com/Microsoft/TypeScript/blob/master/tests/cases/conformance/externalModules/amdImportAsPrimaryExpression.ts

إذا لم تكن هذه طريقة لتحديد الخيارات لكل ملف ، فقد نحتاج إلى شيء جديد. مطلوب تحديد هذا الخيار لكل ملف ، _ على الأقل _ لملفات .d.ts . قد يكون هناك تعليق منسق خاص في الجزء العلوي من الملف ، بعد كل شيء لدينا بالفعل دعم لـ /// <amd-dependency /> and co.

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

هناك أيضًا قيود عملية في بعض الحالات - فهي ليست الأكثر عملية بالنسبة لبعض الوسائط مثل ميزات ES6 ، لأنها تتطلب إعادة تحليل الملف بالكامل. و IIUC يتم فحص النوع بنفس مسار إنشاء AST في المقام الأول.

إذا نظرت إلى كود المحلل اللغوي ، فستتم قراءة التعليقات /// <amd-dependency /> و /// <amd-module /> قبل تحليل أي شيء آخر في الملف. سيكون من الممكن إضافة شيء مثل /// <non-null-types /> . حسنًا ، هذا تسمية فظيعة ولست مع تكاثر خيارات "السحر" العشوائية ولكن هذه مجرد فكرة.

@ jods4 أعتقد أن I 'm pinball تقول أن IDEs لن تدعمها بدون تغييرات كبيرة في كيفية عمل تمييز بناء الجملة.

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

أخبار سارة للجميع ، يتمتع TypeScript 1.6 بقوة تعبيرية كافية لتصميم قيمة مفقودة وتتبعها بأمان (والتي يتم ترميزها بواسطة قيم null و undefined ). يمنحك النمط التالي بشكل أساسي أنواعًا غير قابلة للإلغاء:

declare module Nothing { export const enum Brand {} }
interface Nothing { 'a brand': Nothing.Brand }
export type Nullable<a> = a | Nothing;
var nothing : Nothing = null;
export function isNothing(value: Nullable<a>): value is Nothing {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;
if (isNothing(something)) {
    // missing value
    // there is no way you can get anything out of it
    // there is also NO WAY to get a null reference exception out of it
    // because it doesn't have any methods or properties that could be examined
    // it is 100% explicit and typesafe to use
} else {
    // value is present, it is 100% GUARANTEED being NON-NULL
    // you just CANT get a null reference exception here either
    console.log(something.toLowerCase());
}

/** turns any unsafe values into safe ones */
export function sanitize<a>(unsafe: a) : Nullable<a> {
    return unsafe;
}

var safe = sanitize(toResultFromExternalCodeYouCannotTrust()); // <-- 100% safe to use

مع ذلك ، يجب إغلاق الطلب لأنه لا توجد مشكلة بعد الآن. أُغلِقَت القضية ، ورُفضت الدرجة.

@ aleksey-bykov

مع ذلك ، يجب إغلاق الطلب لأنه لا توجد مشكلة بعد الآن.

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

لن أقوم بلف كل var x: number في الكود الخاص بي إلى Nullable<number> أو حتى أفضل NonNullable<x> . هذا مجرد الكثير من النفقات العامة. ومع ذلك ، أريد أن أعرف أن القيام بـ x *= 2 آمن بنسبة 100٪ ، في أي مكان يحدث هذا في الكود الخاص بي.

لا يحل الرمز الخاص بك مشكلة استخدام مكتبة الطرف الثالث. لن أتصل بـ sanitize على _ كل مكتبة تابعة لجهات خارجية ، أو حتى واجهة برمجة تطبيقات DOM مضمّنة ، والتي أتصل بها. علاوة على ذلك ، لا أريد إضافة isNothing(safe) _ الكل فوق المكان_. ما أريده هو القدرة على الاتصال بـ let squares = [1,2,3].map(x => x*x) والتأكد بنسبة 100٪ في وقت التجميع أن squares.length آمن. مع _any_ API التي أستخدمها.

وبالمثل ، أريد توثيقًا لمكتبات JS للجهات الخارجية بأن الوظيفة تقبل null كمدخلات أم لا. أريد أن أعرف في وقت الترجمة ما إذا كان $(element).css(null) ما يرام أم أنه خطأ.

أريد أن أعمل في بيئات جماعية ، حيث لا يمكنني التأكد من أن كل شخص يستخدم أنماطًا معقدة مثل أنماطك باستمرار. لا يقوم نوع Nulllable الخاص بك بأي شيء على الإطلاق لمنع مطور البرامج من عمل let x: number = null; x.toString() (أو شيء أقل غباء ، ولكن لنفس التأثير).

وهلم جرا وهكذا دواليك. هذه التذكرة _ بعيدة_ من مغلقة ولا تزال المشكلة قائمة بنسبة 100٪.

لا يمكنك أن تكون جادا ، أليس كذلك؟

أنا جاد جدا.

لن أختتم كل واحدة ...

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

أو حتى أفضل NonNullable

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

هذا مجرد الكثير من النفقات العامة.

أين؟

لا يحل الرمز الخاص بك مشكلة استخدام مكتبة الطرف الثالث.

نعم هو كذلك.

لن أقوم باستدعاء التطهير في كل مكتبة تابعة لجهات خارجية ، أو حتى واجهة برمجة تطبيقات DOM مدمجة ، والتي أسميها.

لا داعي لذلك. كل ما عليك فعله هو إصلاح ملف تعريف lib الخاص بالطرف الثالث عن طريق استبدال كل شيء يمكن أن يكون فارغًا بـ Nullable<*>

وبالمثل ، أريد توثيقًا لمكتبات JS التابعة لجهات خارجية والتي تقبل الدالة قيمة فارغة كمدخلات أم لا.

نفس الشيء. حدد هذه الطريقة على أنها قبول Nullable<string> بدلاً من string عادي.

النوع Nulllable الخاص بك لا يفعل شيئًا على الإطلاق لمنع أحد المطورين من عمل let x: number = null؛ x.toString ()

إنه ليس كذلك بالفعل. تحتاج إلى حظر الكلمة الأساسية null باستخدام linter.

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

في 1.4 و 1.5 ، لا يسمح نوع الفراغ لأي أعضاء من الكائن بما في ذلك مثل .toString () ، لذلك يجب أن يكون type Nothing = void; كافيًا بدلاً من الحاجة إلى الوحدة (ما لم يتغير هذا مرة أخرى في 1.6). http://bit.ly/1OC5h8d

@ aleksey-bykov هذا ليس صحيحًا حقًا. لا يزال يتعين عليك توخي الحذر لتعقيم جميع القيم التي من المحتمل أن تكون خالية. لن تتلقى تحذيرًا إذا نسيت القيام بذلك. إنه تحسن بالتأكيد (أقل الأماكن التي يجب توخي الحذر فيها).

أيضًا ، لتوضيح كيف أن الاختراقات لن تصل بك بعيدًا ، فقط حاول

if (isNothing(something)) {
  console.log(something.toString())
}

لن أختتم كل واحدة ...

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

حسنًا ، يمكنني أن أفعل ذلك إذا تم حل كل شيء آخر. وإذا تم حل كل شيء ، فربما يمكن لـ TS أن تفكر في بعض السكر اللغوي لذلك.

أو حتى أفضل NonNullable

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

ليس الأمر أنني أريد فقط التخلص من الاستثناءات الفارغة. أريد أن أكون قادرًا على التعبير عن الأنواع غير القابلة للإلغاء أيضًا ، بأمان ثابت.
لنفترض أن لديك طريقة تُرجع دائمًا قيمة. مثل toString() تُرجع دائمًا سلسلة غير فارغة.
ما هي خياراتك هناك؟
let x: string = a.toString();
هذا ليس جيدًا لأنه لا يوجد تحقق ثابت من أن x.length آمن. في هذه الحالة هو، ولكن كما قلت المدمج في string قد يكون جيدا null بحيث هو _status quo_.
let x = sanitize(a.toString());
حسنًا الآن لا يمكنني استخدامه بدون فحص فارغ ، لذا فإن الرمز _is_ آمن. لكني لا أريد إضافة if (isNothing(x)) كل مكان أستخدم فيه x في الكود الخاص بي! إنه قبيح وغير فعال على حد سواء ، حيث يمكنني أن أعرف جيدًا أن x ليس فارغًا في وقت الترجمة. يعد القيام بـ (<string>x).length أكثر فاعلية ، لكنه لا يزال قبيحًا أن تفعل ذلك في أي مكان تريد فيه استخدام x .

ما أريد أن أفعله هو:

let x = a.toString(); // documented, non-null type string (string! if you want to)
x.length; // statically OK

لا يمكنك تحقيق ذلك بدون دعم اللغة المناسب ، لأن جميع الأنواع في JS (و TS 1.6) لاغية دائمًا.

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

هذا مجرد الكثير من النفقات العامة.

أين؟

انظر إجابتي السابقة.

لا يحل الرمز الخاص بك مشكلة استخدام مكتبة الطرف الثالث.

نعم هو كذلك.

تم حل نصف المشكلة فقط. بافتراض أن تعريفات المكتبة تم تحديثها كما اقترحت: يتم استبدال كل مدخلات أو قيمة مُعادة فارغة بـ Nullable<X> بدلاً من X.

لا يزال بإمكاني تمرير null إلى دالة لا تقبل معلمات null . حسنًا ، هذه الحقيقة موثقة الآن (والتي يمكننا فعلها بالفعل مع تعليقات JSDoc أو أي شيء آخر) ولكني أريدها _فرضية_ في وقت الترجمة.
مثال: declare find<T>(list: T[], predicate: (T) => bool) . يجب ألا تكون كلتا المعلمتين فارغتين (لم أستخدم Nullable ) ومع ذلك يمكنني أن أفعل find(null, null) . خطأ آخر محتمل هو أن الإعلان ينص على أنني يجب أن أعيد قيمة غير خالية bool من المسند ، ومع ذلك يمكنني أن أفعل: find([], () => null) .

النوع Nulllable الخاص بك لا يفعل شيئًا على الإطلاق لمنع أحد المطورين من عمل let x: number = null؛ x.toString ()

إنه ليس كذلك بالفعل. تحتاج إلى حظر الكلمة الأساسية الفارغة باستخدام linter.

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

من وجهة نظري هذه ليست 95٪. أشعر أن بناء الجملة قد ازداد سوءًا بالنسبة للعديد من الأشياء الشائعة وما زلت لا أمتلك كل الأمان الثابت الذي أريده عندما أتحدث عن الأنواع غير القابلة للإلغاء.

لا يزال يتعين عليك توخي الحذر لتعقيم جميع القيم التي من المحتمل أن تكون خالية.

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

لن تتلقى تحذيرًا إذا نسيت القيام بذلك.

نفس الشيء. نفس الشيء.

وضح كيف لن تصلك الاختراقات بعيدًا ، فقط حاول

على الرغم من أنك محق في الطريقة التي حددت بها لا شيء يحتوي على toString من Object ، لكن .. إذا أخذنا فكرة Arnavion (باستخدام void مقابل Nothing ) ينقر فجأة

يشاهد

image

من وجهة نظري هذه ليست 95٪. أشعر أن بناء الجملة قد ازداد سوءًا بالنسبة للعديد من الأشياء الشائعة وما زلت لا أمتلك كل الأمان الثابت الذي أريده عندما أتحدث عن الأنواع غير القابلة للإلغاء.

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

@ aleksey-bykov
أرى ما تحاول القيام به ، على الرغم من أنه يبدو لي أنك لم تسوي جميع القضايا حتى الآن ويبدو أنك تختلق الحلول على طول الطريق عندما يشير شخص ما إلى وجود فجوة (مثل استبدال null بواسطة void في المثال الأخير).

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

في النهاية ، قد تنجح في التواء نظام النوع لتجنب النوع المضمن null ولديك الشيكات التي نريد إرفاقها بنوع Nothing الخاص بك.

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

في هذه المرحلة ، ألا تشعر أن اللغة يجب أن تدعمها فقط؟

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

هل ما زلت أتمنى أن يكون لدينا non-nullables في TypeScript؟ شيء أكيد أفعله.

إذن لماذا تريد إغلاق هذه القضية؟ : مبتسم:

مع ذلك ، يجب إغلاق الطلب لأنه لا توجد مشكلة بعد الآن. أُغلِقَت القضية ، ورُفضت الدرجة.

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

@ aleksey-bykov

لتبسيط

var nothing: void = null;
function isNothing<a>(value: a | void): value is void {
    return value == null;
}
var something = Math.random() > 0.5 ? 'hey!' : nothing;

والآن يمكنك استخدامه بدون أي أغلفة للوظائف أيضًا - تعريفات النوع المناسبة كافية.

هذا هو الحل الذي جعلني في الأصل سعيدًا نوعًا ما حتى تم تثبيطه عن الاعتماد عليه .

سيكون عليك مراجعة جميع ملفات التعريفات الخاصة بك ووضعها! حيثما كان ذلك مناسبا. كيف يختلف ذلك؟

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

أنا أدافع عن " --noImplicitNull ". سيؤدي ذلك إلى جعل جميع الأنواع افتراضية إلى غير قابلة للإلغاء ، وإعطاء ملاحظات على الفور حول المشكلات المحتملة في أي رمز موجود. إذا كان هناك "!" ، يجب أن يتصرف مثل Swift لجعل مدقق الحروف يتجاهل إمكانية القيم الخالية (إرسال غير آمن إلى غير قابل للإلغاء) - على الرغم من أن هذا يتعارض مع ES7 + "إرسال نهائي" على وعود لذلك قد لا يكون الأفضل فكرة.

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

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

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

يقارن:
1
2

  • إذا تم حظر a|void فيمكن للمرء التبديل إلى a|Nothing

يتمثل الاختلاف الرئيسي عند استخدام ميزات اللغة الرسمية القياسية بدلاً من الاختراقات في أن المجتمع (نوع بالتأكيد) موجود للمساعدة في كتابة واختبار ومشاركة ملفات التعريف

  • لا أستطيع أن أرى لماذا يعد استخدام ميزات شرعية بنسبة 100٪ اختراقًا
  • لا تبالغ في تقدير مهارات المجتمع ، فهناك العديد من التعريفات (إن لم يكن معظمها) منخفضة الجودة وحتى تلك التعريفات مثل jquery لا يمكن استخدامها في كثير من الأحيان كما هو مكتوب دون بعض التعديلات
  • مرة أخرى ، لن يكون النمط جيدًا مثل ميزة كاملة ، آمل أن يحصل عليه الجميع ، بلا شك
  • يمكن للنمط أن يحل مشاكلك اليوم في حين أن الميزة قد تكون أو لا تكون قريبة
  • إذا كان من الممكن حل الموقف بكفاءة (تصل إلى 95٪) باستخدام AB و C ، فلماذا يحتاج أي شخص D لفعل الشيء نفسه؟ اشتهاء بعض السكر؟ لماذا لا نركز على شيء لا يمكن حله بنمط؟

على أي حال ، هناك من يحلون المشاكل والكمال ، كما تبين أن المشكلة قابلة للحل

(إذا كنت لا تريد الاعتماد على نوع الفراغ ، فلا يمكن أن يكون أي شيء class Nothing { private toString: any; /* other Object.prototype members */ } والذي سيكون له نفس التأثير. انظر # 1108)

_ملاحظة: لا شيء هنا يتعلق بتدوين بناء الجملة. من فضلك لا تأخذها
كما._

سأضع الأمر ببساطة: إليك مشكلة حل TS 1.6 المقترح
هنا: يمكن أن يعمل بشكل عملي ، لكنه لا ينطبق على المكتبة القياسية أو
معظم ملفات التعريف. كما أنه لا ينطبق على العمليات الحسابية. إن أردت
للشكوى من النوع الزائد / وقت التشغيل boilerplate ، حاول أن تفعل ذلك بشكل عام
المجموعات تعمل في C ++ - وهذا يتضاءل بالمقارنة ، خاصة إذا كنت تريد
التكرار البطيء أو المجموعات غير المصفوفة (أو الأسوأ من ذلك ، حاول
الجمع بين الاثنين).

مشكلة الحل المقترح حاليًا من قبل @ aleksey-bykov هي أن
لم يتم فرضه في جوهر اللغة. على سبيل المثال ، لا يمكنك فعل ذلك
Object.defineProperty(null, "name", desc) - ستحصل على TypeError من
الهدف null . شيء آخر: لا يمكنك تخصيص أي خاصية لملف
كائن null ، مثل var o = null; o.foo = 'foo'; . ستحصل على ملف
المرجع خطأ IIRC. لا يمكن لهذا الحل المقترح تفسير تلك الحالات.
هذا هو السبب في أننا بحاجة إلى دعم لغوي لذلك.


_الآن ، القليل من سفرة الدرّاجات ...

أما بالنسبة إلى النحو ، فأنا أحب إيجاز "؟ سلسلة" و
بناء جملة "! string" ، ولكن بالنسبة للنتيجة النهائية ، لا يهمني ما دمت
عدم كتابة ThisIsANullableType<T, U, SomeRidiculousAndUnnecessaryExtraGeneric<V, W>> .

في الأربعاء ، 29 يوليو 2015 ، الساعة 16:10 كتب أليكسي بيكوف إخطارات github.com:

  • إذا تم حظر | الفراغ ، فيمكن للمرء التبديل إلى | لا شيء

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

    لا أستطيع أن أرى لماذا يعد استخدام ميزات شرعية بنسبة 100٪ اختراقًا

    لا تبالغ في تقدير مهارات المجتمع ، كثير (إن لم يكن معظمهم)
    هناك تعريفات ذات جودة منخفضة وحتى تلك التي تشبه ، على سبيل المثال ، jquery تمامًا

    غالبًا لا يمكن استخدامها كما هو مكتوب بدون بعض التعديلات

    مرة أخرى ، لن يكون النمط جيدًا مثل ميزة كاملة ، الأمل

    الجميع يحصل عليه ، بلا شك

    يمكن للنمط أن يحل مشاكلك اليوم في حين أن الميزة قد أو ربما

    لا تكون قريبة

    إذا كان من الممكن حل الموقف بكفاءة (تصل إلى 95٪) باستخدام AB
    و C لماذا يحتاج أي شخص D لفعل الشيء نفسه؟ اشتهاء بعض السكر؟ لماذا أراد
    نحن نركز على شيء لا يمكن حله بنمط؟

على أي حال ، هناك من يحلون المشاكل والكمال ، كما هو موضح في
المشكلة قابلة للحل

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -126082053
.

وهل يمكننا على الأقل الحصول على فكرة عن شكل هيكل سقيفة الدراجات قبل أن نفكر في اللون الذي نرسمه؟ معظم ما رأيته حتى الآن بعد أول 10 تعليقات هو "مرحبًا ، نريد بناء كوخ للدراجات! ما هو اللون الذي يجب أن نرسمه؟" لا شيء يتعلق بالتأكد من أن التصميم سليم من الناحية الهيكلية. (انظر # 3192 والنصف الأول من # 1206 للحصول على مثالين آخرين لهذا - تلاشت معظم الضوضاء عندما قدمت أخيرًا اقتراحًا جادًا ببنية تم إنشاؤها منطقيًا ودلالات محددة تمامًا.)

ضع في اعتبارك: من المحتمل جدًا أن يؤدي هذا إلى إعادة بناء كبيرة وإعادة كتابة جزئية لتعريفات أنواع المكتبة القياسية. سينتج أيضًا عن غالبية تعريفات النوع الخاصة بـ "DefinitelyTyped" التي تحتاج إلى إعادة كتابتها للإصدار الأول من TS الذي يدعم ذلك. لذا ضع في اعتبارك أن هذا سيكون بالتأكيد كسر. (يمكن أن يكون الحل دائمًا هو الإرسال افتراضيًا ، حتى عند حدوث أخطاء متعلقة بالقيم الصفري ، ولكن لتوفير علامة لا --noImplicitAny لتغيير هذا السلوك.)

من المسلم به أنني فوق رأسي قليلاً هنا. ومع ذلك ، فقد بحثت في scholar.google عن "جافا سكريبت nullable" و "nullability javascript":
فهم TypeScript
يحتوي على جدول جيد للأنواع في TypeScript (نوع الوحدة النمطية).

الأنواع التابعة لجافا سكريبت
يبدو أنه مهم. اختفت شفرة المصدر

ثق ، لكن تحقق: الكتابة على مرحلتين للديناميكيةاللغات
أنواع التنقية للغات البرمجة
يقدم حلاً يعتمد على الكتابة المطبوعة.
("الاختصار t؟ يشير إلى t + null." ؛ يبدو مثل # 186)

afrische يتم استخدام الكثير من هذا عمليا في لعبة الداما من نوع JS. يستخدم Flow معظم التعبيرات الاصطلاحية في "Trust، but Verify" ، على سبيل المثال. هناك أيضًا Infernu ، وهو مدقق من نوع WIP JS الذي يعتمد إلى حد كبير على استدلال نوع Hindley-Milner 1 لاستنتاج أنواعه. لكني استطرادا ...

[1] تستخدم Haskell و OCaml وما إلى ذلك أيضًا نسخة معدلة لأنظمة الأنواع الخاصة بهم.

أنا ألعب حاليًا مع TS fork الذي يفترض أن الأنواع غير قابلة للإلغاء افتراضيًا وهذا يجعل النوع Null (الحالي) قابلاً للإحالة مع null ، على سبيل المثال string | null . لقد أضفت أيضًا بناء الجملة لـ string? وهو مجرد اختصار لنوع الاتحاد نفسه.

عندما تفكر مليًا في الأمر ، فهو نفس الشيء مثل @ aleksey-bykov ، ولكن حيث null هو النوع Nothing ، المدمج.

وهذا ليس معقدًا لأن نظام الكتابة جيد بالفعل في التعامل مع كل هذا!

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

فكرة afrische لاستخدام string? في مشروعك ولكن استخدام string! في .d.ts قد تكون طريقة جديدة. على الرغم من أنني لا أحب تمامًا الازدواجية التعسفية التي تخلقها. لماذا يكون string فارغًا وبعض الملفات غير قابل للإلغاء في ملفات أخرى؟ يبدو غريبًا.

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

هذا الموضوع عمره أكثر من عام ومن الصعب معرفة كل التصميمات والمخاوف ذات الصلة بحوالي 300 تعليق وقليل فقط من فريق TS أنفسهم.

أخطط لترحيل قاعدة بيانات كبيرة إلى Flow أو Closure Compiler أو TypeScript ، لكن الافتقار إلى الأمان الفارغ في TypeScript يمثل عقبة حقيقية في التعامل.

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

عرض

declare var foo:string // nullability unspecified
declare var foo:?string // nullable
declare var foo:!string // non-nullable

مع ?string مجموعة شاملة من !string ، و string مجموعة شاملة وفرعية من كليهما ، أي العلاقة بين string وكلاهما ?string و !string هي نفسها العلاقة بين any وجميع الأنواع الأخرى ، أي النوع المجرد بدون ! أو ? يشبه any فيما يتعلق بالبطلان.

تطبق القواعد التالية:

| اكتب | يحتوي على | يوفر | يمكن تعيين null ؟ | لا يمكن أن نفترض لا null ؟ |
| --- | --- | --- | --- | --- |
| T | T ، !T ، ?T | T ، ?T ، !T | نعم | نعم |
| ?T | T ، !T ، ?T | T ، ?T | نعم | لا (نوع الحارس مطلوب) |
| !T | T ، !T | T ، ?T ، !T | لا | نعم |

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

مثال

إليك بعض الكود مع خطأ مرجعي فارغ:

function test(foo:Foo) { foo.method(); }
test(null);

هذا الرمز لا يزال يمر. null لـ Foo و Foo لا يزال من المفترض أن يكون غير فارغ.

function test(foo:!Foo) { foo.method(); }
test(null);

الآن test(null) خاطئ ، لأن null غير قابل للتخصيص لـ !Foo .

function test(foo:?Foo) { foo.method(); }
test(null);

الآن foo.bar() خاطئ ، لأن استدعاء الأسلوب على ?Foo غير مسموح به (أي يجب أن يتحقق الرمز من null ).

شكرا لك! وأنا حقا أحب هذه الفكرة. على الرغم من أنه من أجل التوافق ،
هل يمكننا إنشاء ?T و T فقط أسماء مستعارة ، مثل Array<T> و T[] ؟ هو - هي
من شأنه أن يبسط الأمور ، ولا تزال النسخة غير المزخرفة من الناحية الفنية
لاغية.

في الجمعة ، 28 أغسطس ، 2015 ، 07:14 كتب جيسي شالكين [email protected] :

هذا الخيط عمره أكثر من عام ومن الصعب معرفة كل ملفات
التصاميم والاهتمامات ذات الصلة مع ما يقرب من 300 تعليق وقليل فقط
من فريق TS أنفسهم.

أخطط لترحيل قاعدة بيانات كبيرة إلى Flow أو Closure Compiler أو
TypeScript لكن الافتقار إلى نوع الأمان في TypeScript يمثل عقبة حقيقية في التعامل.

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

أعلن var foo: string // nullability unspecifieddeclare var foo:؟ string // nullable ، أعلن var foo:! string // non-nullable

مع "سلسلة" مجموعة شاملة من! سلسلة ، وسلسلة كل من مجموعة فرعية ومجموعة فرعية
كلاهما ، أي العلاقة بين السلسلة وكلاهما؟ string و! string
هي نفس العلاقة بين أي وجميع الأنواع الأخرى ، أي أ
نوع عارية بدون! أو ؟ يشبه أي فيما يتعلق بالبطلان.

تطبق القواعد التالية:
النوع يحتوي على توفير هل يمكن تعيينه فارغًا؟ لا يمكن أن نفترض لا باطل؟ TT ،! T ،؟ TT ،
؟ T،! T نعم نعم؟ TT،! T،؟ TT،؟ T نعم لا (اكتب الحارس مطلوب)! TT،! TT،
؟ T،! T لا نعم

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

إليك بعض الكود مع خطأ مرجعي فارغ:

اختبار الوظيفة (foo: Foo) {foo.method () ؛ } test (null)؛

هذا الرمز لا يزال يمر. لا يزال من الممكن تخصيص قيمة null لـ Foo ولا يزال بإمكان Foo
يفترض أن تكون غير خالية.

اختبار الوظيفة (foo:! Foo) {foo.method () ؛ } test (null)؛

الآن الاختبار (فارغ) خاطئ ، لأن القيمة null لا يمكن تخصيصها لـ! Foo.

اختبار الوظيفة (foo:؟ Foo) {foo.method ()؛ } test (null)؛

الآن foo.bar () خاطئ ، لأن استدعاء الأسلوب قيد التشغيل؟ Foo غير مسموح به
(على سبيل المثال ، يجب التحقق من الرمز فارغًا).

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135743011
.

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

شكرا لك! وأنا حقا أحب هذه الفكرة. على الرغم من أنه ، من أجل التوافق ، هل يمكننا إنشاء ?T و T مجرد أسماء مستعارة ، مثل Array<T> و T[] ؟ من شأنه أن يبسط الأمور ، ولا تزال النسخة غير المزخرفة باطلة تقنيًا.

الفكرة الرئيسية هي أن T و ?T و !T هي ثلاثة أشياء مميزة ، وهذا _ هو_ من أجل التوافق (التوافق مع رمز TS الحالي). آسف ، لا أعرف كيف أشرح ذلك بشكل أفضل. أعني ، لقد صنعت طاولة صغيرة وكل شيء.

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

في الجمعة ، 28 أغسطس 2015 ، 11:43 كتب Jesse Schalken [email protected] :

impinball https://github.com/impinball

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

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135810311
.

آسف لكونك حمارًا ذكيًا ، لكن هذا غير منطقي

?string مجموعة شاملة من !string ، و string كلاهما مجموعة شاملة وفرعية من كليهما

من نظرية المجموعة نعرف أن المجموعة أ هي مجموعة فرعية وأن المجموعة الشاملة للمجموعة ب تكون عندما وفقط عندما تكون أ = ب

إذا كان الأمر كذلك ، فعندما تقول "كلاهما" يمكن أن تعني فقط كل شيء من سلسلة = كل شيء من سلسلة وكل شيء من سلسلة!

لذلك في نهاية المطاف كل شيء من؟ سلسلة = كل شيء من! سلسلة

من حيث ينزل الجميع ، الحافلة لا تذهب إلى أي مكان ، الحافلة خارج الخدمة

@ aleksey-bykov على الأرجح حالة من الصياغة السيئة. أعتقد أنه يعني أن string يشبه ?string | !string ، مع مراعاة القيود الأكثر تساهلاً.

يتشابه هذا الأمر برمته في النطاق مع التعليقات التوضيحية لنوع وقت الترجمة @Nullable و @NonNull / @NotNull الشائعة بشكل متزايد في Java ، وكيفية عملها مع الأنواع غير المشروحة.

وسأؤيد بالتأكيد علمًا جديدًا للأنواع التي يتم اعتبارها ضمنيًا على أنها غير قابلة للإلغاء ، ولا سيما البدائية.

قد يكون وضع علامة "non-nullable افتراضيًا" أمرًا رائعًا ، ولكنه سيؤدي أيضًا إلى كسر عدد كبير من التعريفات من نوع DefinitelyTyped. يتضمن ذلك تعريفات Angular و Node هناك ، وسيتطلب الأمر الكثير من العمل الشاق لإصلاحه. قد يتطلب أيضًا منفذًا خلفيًا ، لذلك لا تزال الأنواع الجديدة تحلل ليس كأخطاء في بناء الجملة ، ولكن لم يتم التحقق من قابلية العدول. سيكون مثل هذا المنفذ الخلفي هو الطريقة العملية الوحيدة لتخفيف الكسر باستخدام مثل هذا العلم ، خاصةً مع تحديث التعريفات للأنواع القابلة للقيمة الفارغة. (لا يزال الأشخاص يستخدمون TypeScript 1.4 في التطوير ، خاصة في المشاريع الكبيرة.)

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

لا يزال الأشخاص يستخدمون TypeScript 1.4 في التطوير ، خاصة في المشاريع الكبيرة.

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

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

راجع للشغل أنا مندهش من بقاء الناس على 1.4 ، هناك الكثير لتحبه في 1.5.

@ aleksey-bykov

آسف لكونك حمارًا ذكيًا ، لكن هذا غير منطقي

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

يمكن تعيين أي نوع any (يتصرف كمجموعة شاملة من جميع الأنواع) ومعاملته كأي نوع (يتصرف كمجموعة فرعية من جميع الأنواع). يعني any "إلغاء الاشتراك في التحقق من النوع لهذه القيمة".

يمكن تعيين string من كل من !string أو ?string (يتصرف كمجموعة شاملة منهم) ويمكن معاملته على أنهما !string و ?string (يتصرف كمجموعة فرعية منهم). يعني string "الانسحاب من فحص هذه القيمة" ، أي سلوك TS الحالي.

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

وسأؤيد بالتأكيد علمًا جديدًا للأنواع التي يتم اعتبارها ضمنيًا على أنها غير قابلة للإلغاء ، ولا سيما البدائية.

RyanCavanaugh قال صراحة:

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

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

هذا هو السبب في أن اقتراحي يحافظ على السلوك الحالي للأنواع التي تفتقر إلى محدد إلغاء القيمة ( ! أو ? ). العلامة الآمنة _only_ المراد إضافتها هي العلامة التي لا تسمح للأنواع التي تفتقر إلى محدد عدم القدرة ، أي أنها تأخذ رمز المرور فقط وتتسبب في حدوث خطأ ، ولا تغير معناها. أفترض أنه تم السماح باستخدام noImplicitAny لنفس السبب.

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

قصدت أنه في سياق المتغيرات المكتوبة ضمنيًا لم تتم تهيئة
إلى null أو undefined في مثل هذه الحالات:

var a = new Type(); // type: !Type
var b = 2; // type: !number
var c = 'string'; // type: !string
// etc...

اسف لخلط الامور.

في الجمعة ، 28 أغسطس 2015 ، الساعة 19:54 كتب Jesse Schalken [email protected] :

impinball https://github.com/impinball

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

RyanCavanaugh https://github.com/RyanCavanaugh قال صراحة:

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

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

لهذا السبب يحافظ اقتراحي على السلوك الحالي للأنواع التي تفتقر إليها
محدد الإبطال (! أو؟). ستكون العلامة الآمنة _only_ المراد إضافتها هي
النوع الذي لا يسمح للأنواع التي تفتقر إلى محدد القابلية ، أي فقط
يأخذ رمز المرور ويسبب خطأ ، فإنه لا يغير معناه. أنا
افترض أنه تم السماح بعدم وجود أي ضمني لنفس السبب.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135916676
.

jeffmcaffer ما تحاول قوله عن طريق تغيير المعنى الأصلي للكلمات مجموعة شاملة وفرعية لا يزال من الممكن توضيحها من حيث المجموعات بسهولة: مجموعة القيم string هي اتحاد قيم !string و ?string يعني أي شيء من !string أو / و ?string ينتمي إلى string

impinball مرة أخرى ، RyanCavanaugh ) غير مسموح به. export var b = 5; سيصدر الآن !number حيث سبق له تصدير number .

نعم ، بمعنى ما. لكي تكون أكثر تقنية ، فهي تقبل الاتحاد ، لكن
يوفر التقاطع. بشكل أساسي ، يمكن اعتبار أي من النوعين كملف
يمكن تمرير string و string لأي من النوعين.

في الجمعة ، 28 آب (أغسطس) 2015 ، الساعة 20:20 كتب أليكسي بيكوف إخطارات github.com:

impinball https://github.com/impinball ما تحاول قوله
لا يزال تغيير المعنى الأصلي للكلمات مجموعة فرعية وفرعية
يتم التعبير عنها من حيث المجموعات بسهولة: مجموعة قيم السلسلة هي أ
_union_ لقيم! string و؟ string تعني أي شيء من! string
أو / و؟ سلسلة تنتمي إلى سلسلة

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920233
.

من الواضح أنه ليس من المفترض أن يتم تشغيله افتراضيًا. ومعظم ملفات التعريف
لن تتأثر. من الناحية الفنية ، أي تغيير ليس محضًا
المضافة (حتى إضافة وسيطة جديدة إلى وظيفة ليست في JavaScript) لديها
القدرة على كسر التطبيقات. إنه مشابه في النطاق لـ
noImplicitAny لأنه يفرض كتابة أكثر وضوحًا. و انا
لا أعتقد أنه يمكن أن ينكسر أكثر من ذلك بكثير ، خاصة وأن
الطريقة التي يمكن أن يؤثر بها هذا على الملفات الأخرى هي من خلال الصادرات من TypeScript الفعلي
ملفات المصدر. (قام العلم الآخر بكسر العديد من ملفات التعريف ، وتم تعطيله
طريقة متكررة لاختبار البط.)

في الجمعة ، 28 آب (أغسطس) 2015 ، الساعة 20:21 ، كتب Jesse Schalken [email protected] :

impinball https://github.com/impinball مرة أخرى ، سيتغير هذا العلم
معنى الكود الموجود ، والذي (من قراءةRyanCavanaugh
https://github.com/RyanCavanaugh) غير مسموح به. تصدير فار
ب = 5 ؛ سيصدر الآن! رقم حيث سبق له تصدير رقم.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -135920315
.

من الواضح أنه ليس من المفترض أن يتم تشغيله افتراضيًا.

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

هذا هو السبب في أن اقتراحي يحافظ على السلوك الحالي للأنواع التي تفتقر إلى محدد إلغاء القيمة ( ? أو ! ).

تضمين التغريدة
إذا كنت تريد (وتدير) الحفاظ على معنى الكود الحالي بدقة 100٪ ، فعليك التخلي عن فكرة الأمان الفارغ في الكتابة المطبوعة. لن يكون هناك حل قابل للاستخدام من الناحية العملية.

إن الحاجة إلى وضع ? و ! على كل نوع من التعليقات التوضيحية هي بالفعل مطولة بما يكفي بالنسبة لي لعدم الرغبة في القيام بذلك ، ولكن سيتعين عليك أيضًا التخلي عن كل أنواع الاستدلالات. يستنتج let x = 3 number اليوم. إذا كنت لا تقبل تغييرًا هنا ، فهذا يعني أنه يتعين عليك كتابة كل شيء بشكل صريح لجني فوائد الأمان الفارغ. ليس شيئًا أرغب في فعله أيضًا.

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

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

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

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

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

إذن ماذا عن هذا:

  • باستخدام -noImplicitNull ، يصبح T اسمًا مستعارًا لـ !T . وإلا فإن إبطال T غير معروف.
  • يجب أن يكون العلم قابلاً للتجاوز على أساس كل وحدة عن طريق إضافة تعليق توضيحي في الأعلى ، على سبيل المثال. <strong i="18">@ts</strong>:implicit_null . هذا مشابه للطريقة التي يتيح بها Flow فحص النوع على أساس كل وحدة.
  • يتم تحويل مصدر البرنامج الحالي عن طريق إضافة خيار مترجم noImplicitNull أولاً ، ثم إضافة تعليق توضيحي على جميع الوحدات النمطية الموجودة بعلامة ' implicit_null '. يمكن أن تكون هذه الخطوة الثانية آلية بشكل تافه.
  • عند استيراد وحدة نمطية ، يجب أن تكون هناك سياسة حول كيفية تحويل الأنواع إذا كانت الوحدات النمطية المستوردة والمستوردة لها إعدادات ضمنية مختلفة.

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

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

أفكار؟

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

| كود | علم | المعنى |
| --- | --- | --- |
| interface Foo { blah; } | | interface Foo { blah:any; } |
| interface Foo { blah; } | noImplicitAny | خطأ ، النوع الصريح مطلوب |
| var foo = 'blah' | | var foo:string = 'blah' |
| var foo = 'blah' | noImplicitNull | var foo:!string = 'blah' |

باستخدام noImplicitNull ، قبل أن يكون لديك متغير يمكن الكتابة إليه بقيمة خالية. الآن لديك متغير لا يمكن الكتابة إليه null _. هذا وحش مختلف تمامًا عن noImplicitAny .

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

jesseschalken آسف لكني لا أرى الفرق.
قبل noImplicitAny قد يكون لديك هذا في شفرتك:
let double = x => x*2;
يجمع ويعمل بشكل جيد. ولكن بمجرد تشغيل noImplicitAny ، فإن المترجم يرمي إليك بخطأ قائلًا أن x هو أي شيء ضمنيًا. يجب عليك تعديل الكود الخاص بك لجعله يعمل مع العلم الجديد:
let double = (x: any) => x*2 أو أفضل من let double = (x: number) => x*2 .
لاحظ أنه على الرغم من ظهور خطأ في المترجم ، إلا أنه سيظل يرسل رمز JS يعمل بشكل مثالي (إلا إذا قمت بإيقاف تشغيل البث عند وجود أخطاء).

الوضع مع القيم الخالية إلى حد كبير هو نفسه في رأيي. لنفترض للمناقشة أنه مع العلم الجديد ، T غير قابل للإلغاء و T? أو T | null يشير إلى الاتحاد من النوع T و null .
قبل أن يكون لديك:
let foo: string; foo = null; أو حتى let foo = "X"; foo = null والذي يمكن استنتاجه إلى string بنفس الطريقة.
يجمع ويعمل بشكل جيد. الآن قم بتشغيل العلم الجديد noImplicitNull . فجأة ألقى TS خطأ يشير إلى أنه لا يمكنك تخصيص null لشيء لم يتم الإعلان عنه صراحة على هذا النحو. ولكن باستثناء خطأ الكتابة ، لا يزال كودك يصدر _the نفسه_ ، كود JS الصحيح.
مع العلم ، تحتاج إلى تحديد نيتك بوضوح وتعديل الكود:
string? foo; foo = null;

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

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

robertknight قريب جدًا من تفكيري الحالي.

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

المشكلة هي أن بعض T هي في الواقع غير قابلة للإلغاء وبعضها لاغى. يعتبر:

// In a strict module, function len does not accept nulls
function len(x: string): number { return x.length; }
// In a legacy module, some calls to len
let abc: string = "abc";
len(abc);

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

@ jods4 اقرأ تعليقي مرة أخرى. انظر الى الطاولة. لا أعرف كيف أعبر عنها بشكل أكثر وضوحًا.

تم تصميم المثال الخاص بك بشكل واضح للوصول إلى الاستنتاج الخاص بك عن طريق وضع تعريف foo والمهمة على foo بجانب بعضهما البعض. مع noImplicitAny ، فإن الأخطاء الوحيدة الناتجة هي تحديدًا من الكود الذي يجب تغييره (نظرًا لأنه لم يغير معناه ، فقد تطلب فقط التعبير عنه بشكل أكثر وضوحًا). باستخدام noImplicitNull ، كان الرمز الذي تسبب في الخطأ هو _assignment_ إلى foo لكن الكود المطلوب تغييره لإصلاحه (ليكون له المعنى القديم) هو _ تعريف_ foo . هذا مهم للغاية ، لأن العلم قد _غير معنى تعريف foo _. من الواضح أن المهمة والتعريف يمكن أن يكونا على جوانب مختلفة من حدود المكتبة ، حيث غيّر العلم noImplicitNull معنى_الواجهة العامة للمكتبة!

غير العلم معنى تعريف foo.

نعم هذا صحيح. لقد تغيرت من "ليس لدي أدنى فكرة عما إذا كان المتغير يمكن أن يكون فارغًا أم لا - وأنا فقط لا أهتم" إلى "هذا المتغير خالٍ من الصفر". هناك احتمال بنسبة 50/50 أن يكون ذلك صحيحًا ، وإذا لم يكن كذلك ، فيجب أن تحدد نيتك في الإعلان. في النهاية ، تكون النتيجة مماثلة تمامًا مثل noImplicitAny : يجب أن تجعل نيتك أكثر وضوحًا في الإعلان.

من الواضح أن المهمة والتعريف يمكن أن يكونا على جوانب مختلفة من حدود المكتبة

في الواقع ، عادةً ما يكون الإعلان في المكتبة والاستخدام في الكود الخاص بي. الآن:

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

لا يمكن تشغيل هذه العلامة (تمامًا مثل noImplicitAny ) بدون تعديل الكود الخاص بك.

أرى وجهة نظرك ، لكنني أقول إننا لا _ نغير_ رمز المعنى ؛ بل إننا _ نعبر عن المعنى الذي لا يتناسب مع نظام الكتابة اليوم.

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

بشرى سارة لأنواع غير قابلة للإلغاء! يبدو أن فريق TS لا بأس به في إدخال تغييرات عاجلة في تحديثات TS!
إذا رأيت هذا:
http://blogs.msdn.com/b/typescript/archive/2015/09/02/announcing-typescript-1-6-beta-react-jsx-better-error-checking-and-more.aspx
يقدمون تغييرًا فاصلًا (بناء جملة اختياري حصري للطرفين) بنوع ملف جديد ، ويقدمون تغييرًا مقطوعًا _ بدون _ نوع الملف الجديد (يؤثر على الجميع).
هذه سابقة يمكننا مناقشة الأنواع غير القابلة للإلغاء باستخدام (على سبيل المثال ، امتداد sts.

الآن نحتاج فقط إلى معرفة ما إذا كنا نريد من المترجم أن يحاول التحقق من الأنواع غير المحددة أو الأنواع الفارغة فقط (وأي بناء الجملة) ولدينا اقتراح قوي.

jbondc قراءة ممتعة جدا. يسعدني أن أرى أن حدسي حول الترحيل إلى NNBD (غير قابل للإلغاء افتراضيًا) أسهل من الترحيل إلى اختياري غير قابل للإلغاء تم تأكيده من خلال الدراسات (ترتيب من حيث الحجم أقل لتغييرات الهجرة ، وفي حالة Dart ، 1- يلزم وجود تعليقين توضيحيين لكل 1000 سطر من التعليمات البرمجية لتغييرات في القيمة الصفرية ، وليس أكثر من 10 حتى في التعليمات البرمجية الخالية من القيم الثقيلة)

لست متأكدًا مما إذا كان تعقيد المستند يعكس حقًا تعقيد الأنواع غير القابلة للإلغاء. على سبيل المثال ، في قسم الأدوية العامة ، يناقشون ثلاثة أنواع من معلمات النوع الرسمية ، ثم يوضحون أنك لست بحاجة إليها بالفعل. في TS ، سيكون null ببساطة هو نوع السجل الفارغ تمامًا (بدون خصائص ، ولا توجد طرق) بينما {} سيكون نوع الجذر غير الفارغ ، ومن ثم فإن الأدوية غير القابلة للإلغاء هي ببساطة G<T extends {}> - لا داعي لمناقشة أنواع متعددة من معلمات النوع الرسمية على الإطلاق.

بالإضافة إلى ذلك ، يبدو أنهم يقترحون الكثير من السكر غير الأساسي ، مثل var !x

ومع ذلك ، فإن دراسة اللغات الحالية التي تعاملت مع نفس المشكلة هي الجوهرة الحقيقية.

عند قراءة المستند ، أدركت أن أنواع Optional / Maybe أقوى من الأنواع الفارغة ، خاصة في سياق عام - غالبًا بسبب القدرة على تشفير Just(Nothing) . على سبيل المثال ، إذا كانت لدينا واجهة خريطة عامة تحتوي على قيم من النوع T وتدعم get والتي قد ترجع أو لا ترجع قيمة بناءً على وجود المفتاح:

interface Map<T> {
  get(s:string):Maybe<T>
}

لا يوجد شيء يمنع T من أن يكون من النوع Maybe<U> ؛ سيعمل الرمز بشكل جيد ويعيد Just(Nothing) إذا كان المفتاح موجودًا ولكنه يحتوي على قيمة Nothing ، وسيعيد ببساطة Nothing إذا لم يكن المفتاح موجودًا على الإطلاق.

في المقابل ، إذا استخدمنا أنواع لاغية

interface Map<T> {
  get(s:string):T?
}

ثم من المستحيل التمييز بين مفتاح مفقود وقيمة فارغة عندما يكون T غير صالح.

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

jbondc هذا بحث

أجد مطمئناً أن الدراسات تُظهر أن 80٪ من الإعلانات تعني في الواقع غير فارغة أو أن هناك 20 تعليقًا توضيحيًا باللاغية لكل KLOC (ص 21). كما هو مذكور في المستند ، فهذه حجة قوية لعدم وجود قيمة افتراضية بشكل افتراضي ، وهذا هو شعوري أيضًا.

هناك حجة أخرى لصالح non-null وهي أنها تنشئ نظامًا من النوع الأنظف: null هو نوعه الخاص ، T غير فارغ و T? هو مرادف لـ T | null . نظرًا لأن TS لديها نوع اتحاد بالفعل ، كل شيء لطيف ونظيف ويعمل بشكل جيد.

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

لقد وجدت فكرة المشغل T! مثيرة للاهتمام وربما مفيدة. كنت أفكر في نظام يكون فيه T نوعًا غير فارغ ، و T? هو T | null . لكن ما أزعجني أنه لا يمكنك حقًا إنشاء واجهة برمجة تطبيقات عامة تضمن نتيجة غير فارغة حتى في مواجهة إدخال فارغ. ليس لدي حالات استخدام جيدة ، لكن من الناحية النظرية لا يمكنني تصميم هذا بأمانة: function defined(x) { return x || false; } .
باستخدام عامل الانعكاس غير الفارغ ، يمكن للمرء أن يفعل: function defined<T>(x: T): T! | boolean . بمعنى أنه إذا أرجع defined T فمن المضمون أن يكون غير فارغ ، حتى إذا كان القيد العام T لاغياً ، على سبيل المثال string? . ولا أعتقد أنه من الصعب وضع نموذج في TS: بالنظر إلى T! ، إذا كان T هو نوع اتحاد يتضمن null النوع ، فقم بإرجاع النوع الناتج من إزالة null من الاتحاد.

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

عند قراءة المستند أدركت أن الأنواع الاختيارية / ربما تكون أقوى من الأنواع الفارغة

يمكنك تداخل هياكل Maybe ، لا يمكنك تداخل null ، بالفعل.

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

Maybe بتشفير جزأين متميزين من المعلومات: ما إذا كانت هناك قيمة وما هي القيمة. أخذ مثالك Map والنظر إلى C # هذا واضح ، من Dictionary<T,K> :
bool TryGet(K key, out T value) .
لاحظ أنه إذا كانت C # تحتوي على مجموعات (ربما C # 7) ، فهذا في الأساس هو نفسه:
(bool hasKey, T value) TryGet(K key)
وهو في الأساس Maybe ويسمح بتخزين null .

لاحظ أن JS لها طريقتها الخاصة في التعامل مع هذه المشكلة وأنها تخلق الكثير من المشاكل الجديدة المثيرة للاهتمام: undefined . سيعود JS Map النموذجي undefined إذا لم يتم العثور على المفتاح ، وقيمته ، بما في ذلك null .

الاقتراح ذو الصلة لـ C # 7 - https://github.com/dotnet/roslyn/issues/5032

أنتم تدركون أن المشكلة لم تحل إلا إذا كنتم نموذجًا غير محدد بنفس الطريقة؟
وإلا فسيتم استبدال جميع مشكلاتك الفارغة بمشكلات غير محددة (والتي تكون imo أكثر انتشارًا على أي حال).

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

سيتم استبدال جميع مشكلاتك الفارغة بمشكلات غير محددة

لا ، لماذا هم؟
ستختفي مشاكلي الفارغة وستبقى مشاكلي غير المحددة.

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

أقوم بالكود مع عدم وجود رمز غير محدد تقريبًا باستثناء المكان الذي يفرضه المتصفح عليّ

كنت أعتقد أن JavaScript يفرض undefined على واحد في كل منعطف.

  • المتغيرات غير المهيأة. let x; alert(x);
  • وسيطات الدالة المحذوفة. let foo = (a?) => alert(a); foo();
  • الوصول إلى عناصر مصفوفة غير موجودة. let x = []; alert(x[0]);
  • الوصول إلى خصائص الكائن غير الموجودة. let x = {}; alert(x['foo']);

من ناحية أخرى ، يحدث Null في مواقف أقل وأكثر قابلية للتنبؤ:

  • الوصول إلى DOM. alert(document.getElementById('nonExistent'));
  • استجابات خدمة الويب للجهات الخارجية (منذ JSON.stringify شرائط undefined ). { name: "Joe", address: null }
  • Regex.

لهذا السبب ، نحظر استخدام null ، ونحول كل null المستلمين عبر التحويل إلى undefined ، ونستخدم دائمًا فحصًا صارمًا للمساواة لـ undefined . لقد عمل هذا بشكل جيد بالنسبة لنا في الممارسة.

وبالتالي فأنا أوافق على أن مشكلة undefined هي المشكلة الأكثر انتشارًا.

NoelAbrahams أسلوب الترميز ، أقول لك :)

المتغيرات غير المهيأة

أقوم دائمًا بتهيئة المتغيرات ولدي noImplicitAny قيد التشغيل ، لذا فإن let x سيكون خطأ على أي حال. كلما اقتربت من استخدامه في مشروعي هو let x: any = null ، على الرغم من أنني لن أكتب هذا الرمز كثيرًا.

معلمات الوظيفة الاختيارية

أنا أستخدم قيم المعلمات الافتراضية للمعلمات الاختيارية ، ويبدو لي أن هذا أكثر منطقية (كودك _سوف تقرأ وتستخدم المعلمة بطريقة ما ، أليس كذلك؟). لذلك بالنسبة لي: function f(name?: string = null, options?: any = {}) .
سيكون الوصول إلى قيمة المعلمة الخام undefined حالة _ استثنائية _ بالنسبة لي.

الوصول إلى عناصر مصفوفة غير موجودة

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

الوصول إلى خصائص الكائن غير الموجودة.

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

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

let cats: Cat[];
// Note that find returns undefined if there's no cat named Kitty
let myFavoriteCat = cats.find(c => c.name === 'Kitty'); 
if (myFavoriteCat === undefined) {
  // Immediately do something to compensate here:
  // return false; or 
  // myFavoriteCat = new Cat('Kitty'); or
  // whatever makes sense.
}
// Continue with assurance that myFavoriteCat is not null (it was an array of non-nullable cats after all).

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

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

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

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

@ jods4 ، كان من الممتع قراءة

أعتقد أن الاعتراض الذي لدي على هذا النهج هو أنه يبدو أن هناك الكثير من القواعد التي يجب الالتزام بها - إنها تشبه إلى حد ما الشيوعية مقابل السوق الحرة: ابتسم:

يمتلك فريق TS داخليًا بأسلوبهم الخاص.

NoelAbrahams "استخدام undefined " هي قاعدة مثل "Use null ".

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

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

لماذا أحب أن استخدام null بدلا من undefined :

  1. إنه يعمل بطريقة مألوفة للمطورين لدينا ، يأتي الكثير منهم من لغات OO ثابتة مثل C #.
  2. عادةً ما تُعتبر المتغيرات غير المهيأة بمثابة رمز لرائحة في العديد من اللغات ، ولست متأكدًا من سبب اختلافها في JS. اجعل نيتك واضحة.
  3. على الرغم من أن JS لغة ديناميكية ، إلا أن الأداء يكون أفضل مع الأنواع الثابتة التي لا تتغير. يعتبر تعيين الخاصية إلى null أكثر فاعلية من تعيين الخاصية إلى delete عليها.
  4. وهو يدعم الاختلاف الواضح بين null الذي تم تعريفه ولكنه يدل على عدم وجود القيمة ، و undefined ، وهو ... غير محدد. مكان يكون فيه الاختلاف بين الاثنين واضحًا: المعلمات الاختيارية. ينتج عن عدم تمرير المعلمة undefined . كيف يمكنك _تجاوز _ القيمة الفارغة إذا كنت تستخدم undefined لذلك في قاعدة الشفرة الخاصة بك؟ باستخدام null لا توجد مشكلة هنا.
  5. إنه يضع مسارًا نظيفًا لفحص القيمة الفارغة ، كما تمت مناقشته في هذا الموضوع ، وهو أمر غير عملي حقًا مع undefined . على الرغم من أنني ربما أحلم بيومًا في هذا الأمر.
  6. عليك أن تختار الاتساق ، IMHO null جيد مثل undefined .

أعتقد أن سبب تفضيل undefined على null هو بسبب
الوسائط الافتراضية والاتساق مع إرجاع obj.nonexistentProp
undefined .

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

في الثلاثاء ، 8 سبتمبر 2015 ، 06:48 كتب jods [email protected] :

NoelAbrahams https://github.com/NoelAbrahams "Use undefined" مثل
كثير من القاعدة على أنها "استخدام لاغية".

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

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

لماذا _I_ أحب استخدام القيمة الفارغة بدلاً من غير المعرفة:

  1. إنه يعمل بطريقة مألوفة للمطورين لدينا ، يأتي الكثير منهم من OO ثابت
    لغات مثل C #.
  2. عادة ما تعتبر المتغيرات غير المهيأة رمزًا للرائحة
    العديد من اللغات ، لست متأكدًا من سبب اختلافها في JS. اصنع لك
    نية واضحة.
  3. على الرغم من أن JS لغة ديناميكية ، إلا أن الأداء أفضل معها
    أنواع ثابتة لا تتغير. هو أكثر فعالية لتعيين خاصية ل
    فارغة من حذفها.
  4. وهو يدعم الاختلاف النظيف بين القيمة الفارغة التي تم تعريفها ولكن
    يدل على عدم وجود قيمة ، وغير معرف ، وهو ... غير محدد.
    مكان يكون فيه الاختلاف بين الاثنين واضحًا: اختياري
    المعلمات. عدم اجتياز معلمة ينتج عنه غير محدد. كيف يمكنك
    _pass_ القيمة الفارغة إذا كنت تستخدم غير محدد لذلك في التعليمات البرمجية الخاصة بك
    قاعدة؟ باستخدام null لا توجد مشكلة هنا.
  5. إنه يضع مسارًا نظيفًا لفحص القيمة الفارغة ، كما تمت مناقشته في هذا
    الخيط ، وهو غير عملي حقًا مع غير محدد. على الرغم من أنه ربما
    أنا أحلم بيومًا في هذا.
  6. عليك أن تختار الاتساق ، IMHO null جيد مثل
    غير معرف.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138514437
.

تضمين التغريدة
هل يمكننا التوقف عن استخدام "bikeshedding" في كل مناقشة على جيثب؟ يمكننا أن نقول بأمان أن استخدام undefined أو null هو أحد تفضيلات الفريق ؛ لكن مسألة محاولة تضمين undefined في الشيكات الفارغة (أم لا) وكيف ستعمل ليست تافهة. إذن أنا لا أفهم كيف أن هذا هو حفل زفاف بالدراجات في المقام الأول؟

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

  1. ماذا نفعل بـ undefined ؟ (رأيي: لا يزال في كل مكان وبدون رادع)
  2. كيف نتعامل مع التوافق مع الكود الحالي ، في تعريفات معينة؟ (رأيي: علامة التمكين ، على الأقل لكل ملف تعريف. تشغيل العلامة "معطل" لأنك قد تضطر إلى إضافة تعليقات توضيحية فارغة.)

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

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

في الثلاثاء ، 8 سبتمبر 2015 ، 13:28 كتب jods [email protected] :

impinball https://github.com/impinball
هل يمكننا التوقف عن استخدام "bikeshedding" في كل مناقشة على جيثب؟ نستطيع بأمان
قل أن استخدام غير محدد أو فارغ هو تفضيل الفريق ؛ لكن القضية
ما إذا كان يجب محاولة تضمين غير معرف في الشيكات الفارغة (أم لا) وكيف
سيعمل ليس تافها. لذلك أنا لا أفهم كيف أن هذا هو ركوب الدراجات في
المركز الأول؟

لقد قمت ببناء شوكة TS 1.5 بأنواع non-nullable وكانت كذلك
سهل بشكل مدهش. ولكن أعتقد أن هناك قضيتين صعبتين
بحاجة إلى إجماع على وجود أنواع غير قابلة للإلغاء في مترجم TS الرسمي ،
تمت مناقشة كليهما مطولاً أعلاه دون استنتاج واضح:

  1. ماذا نفعل مع غير محدد؟ (رأيي: لا يزال في كل مكان
    وغير محدد)
  2. كيف نتعامل مع التوافق مع الكود الحالي ، على وجه الخصوص
    تعريفات؟ (رأيي: علامة الاشتراك ، على الأقل لكل ملف تعريف.
    تشغيل العلم هو "كسر" لأنه قد تضطر إلى إضافة قيمة فارغة
    الشروح.)

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -138641395
.

@ jods4 أعتبر من

هل يمكنك ربطه من فضلك؟ وأود أن محاولة الخروج منها.

تضمين التغريدة
أود أن أرى (بعض) الأمان مقابل undefined أيضًا ، لكنه منتشر تمامًا.

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

هل يجب التفريق بين النوعين null و undefined ؟ هذا ليس بالأمر الصعب: T | null ، T | undefined ، T | null | undefined وقد يوفر إجابة سهلة للسؤال أعلاه. ولكن ماذا بعد ذلك عن صيغة الاختزال: ما الذي يمثله T? ؟ كلاهما باطل وغير محدد؟ هل نحتاج إلى اختزالين مختلفين؟

تضمين التغريدة
الأنواع الفارغة وغير المعرفة موجودة بالفعل في TS.
كان رأيي هو:

  1. جعل جميع الأنواع غير قابلة للإلغاء (بما في ذلك الأنواع المستنتجة) ؛
  2. امنح النوع الفارغ اسمًا ( null ) يمكنك استخدامه في تعريفات النوع ؛
  3. إزالة التوسيع من null إلى any ؛
  4. أدخل الاختزال النحوي T? وهو نفس T | null ؛
  5. إزالة التحويلات الضمنية من null إلى أي نوع آخر.

بدون الوصول إلى مصادري أعتقد أن هذا هو جوهر ذلك. يقوم النوع Null الموجود ونظام نوع TS الرائع (خاصة أنواع الاتحاد وحراس النوع) بالباقي.

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

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

لا أفهم كيف يمكن اعتبار هذا حلاً عندما تكون المشكلة غير المحددة أكبر بكثير من القيمة الصفرية!

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

الهاتف دانغ!
*كل واحد

* لقد حدث ما يكفي لدرجة أنني أردت تسليط الضوء عليه.

كما ينبغي أن يكون نص الفقرة الأخيرة كما يلي: "الطريقة الوحيدة لهذا الاقتراح".

@ jods4 أود أن أقول إنها ستعتمد على بعض التركيبات. في بعض الحالات ، يمكنك ضمان عدم الإبطال في تلك الحالات ، مثل ما يلي:

declare const list: T![];

for (const entry of list) {
  // `entry` is clearly immutable here.
}

list.forEach(entry => {
  // `entry` is clearly immutable here.
})

list.map(entry => {
  // `entry` is clearly immutable here.
})

في هذه الحالة ، يجب أن يكون لدى المترجم الكثير من المنطق للتأكد من أن المصفوفة مقيدة بحدود:

declare const list: T![]

for (let i = 0; i < list.length; i++) {
  // This could potentially fail if the compiler doesn't correctly do the static bounds check.
  const entry: T![] = list[i];
}

وفي هذه الحالة ، لا توجد طريقة يمكنك من خلالها ضمان أن المترجم يمكنه التحقق من الوصول للحصول على entry ضمن الحدود دون تقييم أجزاء من الكود فعليًا:

declare const list: T![]

const end = round(max, list.length);

for (let i = 0; i < end; i++) {
  const entry: T![] = list[i];
}

هناك بعض الأشياء السهلة والواضحة ، ولكن هناك بعض الأصعب منها.

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

لكن الوصول إلى المصفوفة الكلاسيكية هو سيناريو مهم وأود أن أرى حلاً جيدًا لذلك. من الواضح أن إجراء تحليل معقد كما اقترحت ليس ممكنًا إلا في حالات تافهة (لكنها حالات مهمة لأنها شائعة). لكن لاحظ أنه حتى لو تمكنت من إثبات أن i < array.length فهذا لا يثبت أن العنصر قد تمت تهيئته.

ضع في اعتبارك المثال التالي ، ماذا تعتقد أن TS يجب أن تفعل؟

let array: T![] = [];  // an empty array of non-null, non-undefined T
// blah blah
array[4] = new T();  // Fine for array[4], but it means array[0..3] are undefined, is that OK?
// blah blah
let i = 2;
// Note that we could have an array bounds guard
if (i < array.length) {
  let t = array[i];  // Inferred type would be T!, but this is actually undefined :(
}

هناك أيضًا مشكلة في Object.defineProperty.

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
  get() { return 10 },
})

في الأربعاء ، 9 سبتمبر 2015 ، 17:49 كتب jods [email protected] :

impinball https://github.com/impinball في الواقع ، واجهة برمجة تطبيقات حديثة مثل الخريطة ،
forEach or for.. of are ok because they تخطي العناصر التي لم تكن أبدا
مهيأ أو محذوف. (وهي تشمل العناصر التي تم ضبطها على
غير محدد ، ولكن TS الافتراضي الآمن لدينا من شأنه أن يمنع ذلك.)

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

ضع في اعتبارك المثال التالي ، ماذا تعتقد أن TS يجب أن تفعل؟

اسمح للمصفوفة: T! [] = []؛ // مجموعة فارغة من T // blah blah
صفيف [4] = جديد T () ؛ // جيد للمصفوفة [4] ، ولكن هذا يعني أن المصفوفة [0..3] غير محددة ، فهل هذا موافق؟ // blah blahlet i = 2 ؛ // لاحظ أنه يمكن أن يكون لدينا مجموعة حدود guardif (i الطول) {
اسمحوا t = مجموعة [i] ؛ // سيكون النوع المستنتج هو T! ، لكن هذا في الواقع غير محدد :(
}

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139055786
.

كل ما عليك فعله هو:

  1. اجعل النوعين Null و Undefined قابلين للإشارة.
  2. منع القيم الفارغة وغير المعرفة من أن تكون قابلة للتخصيص لأي شيء.
  3. استخدم نوع الاتحاد مع Null و / أو Undefined حيث ضمنا مفهوم nullability.

إنه بالفعل تغيير جريء ويبدو أكثر ملاءمة للغة
إطالة.
في 10 سبتمبر 2015 ، الساعة 9:13 صباحًا ، كتب "Isiah Meadows" [email protected] :

هناك أيضًا مشكلة في Object.defineProperty.

let array = new Array(5)
Object.defineProperty(array, "length", 2, {
get() { return 10 },
})

في الأربعاء ، 9 سبتمبر 2015 ، 17:49 كتب jods [email protected] :

impinball https://github.com/impinball في الواقع ، واجهة برمجة التطبيقات الحديثة مثل
خريطة،
forEach or for.. of are ok because they تخطي العناصر التي لم تكن أبدا
مهيأ أو محذوف. (وهي تشمل العناصر التي تم ضبطها على
غير محدد ، ولكن TS الافتراضي الآمن لدينا من شأنه أن يمنع ذلك.)

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

ضع في اعتبارك المثال التالي ، ماذا تعتقد أن TS يجب أن تفعل؟

اسمح للمصفوفة: T! [] = []؛ // مصفوفة فارغة من T غير فارغة وغير محددة //
بلاه بلاه
صفيف [4] = جديد T () ؛ // جيد للمصفوفة [4] ، لكن هذا يعني أن المصفوفة [0..3] هي
غير محدد ، هل هذا جيد؟ // blah blahlet i = 2 ؛ // لاحظ أنه يمكن أن يكون لدينا ملف
حراسة حدود المصفوفة (i <array.length) {
اسمحوا t = مجموعة [i] ؛ // سيكون النوع المستنتج T! ، لكن هذا في الواقع
غير معرف :(
}

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139055786>
.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139230568
.

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

@ aleksey-bykov

يبدو أكثر ملاءمة لتمديد اللغة.

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

أنا أعلم أن المصفوفات المكتوبة لديها ضمان ، لأن IIRC يرمون ملف
مرجع خطأ في تحميل وتخزين مصفوفة خارج الحدود. المصفوفات العادية و
تعيد كائنات الوسائط غير معرفة عندما يكون الفهرس خارج الحدود. في
الرأي ، هذا خطأ في لغة JS ، لكن إصلاحه سيكون بالتأكيد
كسر الويب.

الطريقة الوحيدة "لإصلاح" هذا هو في ES6 ، عبر المُنشئ الذي يعيد الخادم الوكيل ،
ونموذج المثيل والنموذج الأولي الذاتي الخاص به تم تعيينهما على المصفوفة الأصلية
البناء. شيء من هذا القبيل:

Array = (function (A) {
  "use strict";
  function check(target, prop) {
    const i = +prop;
    if (prop != i) return target[prop];
    if (i >= target.length) {
      throw new ReferenceError();
    }
    return i;
  }

  function Array(...args) {
    return new Proxy(new Array(...args), {
      get(target, prop) {
        return target[check(target, prop)];
      },
      set(target, prop, value) {
        return target[check(target, prop)] = value;
      },
    });
  }

  Array.prototype = A.prototype;
  Array.prototype.constructor = Array
  Object.setPrototypeOf(Array, A);
  return Array;
})(Array);

(ملاحظة: لم يتم اختبارها ، أو كتابتها على الهاتف ...)

في الخميس ، 10 سبتمبر 2015 ، الساعة 10:09 كتب jods [email protected] :

impinball https://github.com/impinball
أشعر بالرضا فيما يتعلق بمثالك. استخدام تعريف بهذه الطريقة هو
لا تخطو خارج صندوق الأمان TS إلى عالم JS الديناميكي
أتظن؟ لا أعتقد أنني اتصلت من قبل بـًًًَعرِّف الملكية مباشرة في كود TS.

@ aleksey-bykov https://github.com/aleksey-bykov

يبدو أكثر ملاءمة لتمديد اللغة.

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139245706
.

impinball لست متأكدًا من وجود شيء "لإصلاحه" في المقام الأول ...
تلك هي دلالات JS و TS يجب أن تستوعبهم بطريقة ما.

ماذا لو كان لدينا فقط علامة (مختلفة قليلاً) للإعلان عن مصفوفة متفرقة مقابل مصفوفة غير متفرقة ، وجعلنا المصفوفة غير المتفرقة يتم تهيئتها تلقائيًا (ربما يمكن للمبرمجين توفير قيمة أو معادلة للتهيئة). بهذه الطريقة يمكننا إجبار المصفوفات المتناثرة على أن تكون من النوع T|undefined (والذي سيتغير إلى النوع T باستخدام لـ ... من والعمليات "الآمنة" الأخرى) وترك أنواع المصفوفات غير المتفرقة بمفردها.

//not-sparse
var array = [arrlength] => index*3;
var array = <number[]>[3];
//sparse
var array = [];

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

تضمين التغريدة
لا أعرف ... يصبح الأمر محيرًا.

بهذه الطريقة يمكننا إجبار المصفوفات المتفرقة على أن تكون من النوع T|undefined (والذي قد يتغير إلى النوع T باستخدام ... of والعمليات "الآمنة" الأخرى)

بسبب المراوغة في JS لا تعمل بهذه الطريقة. افترض let arr: (T|undefined)[] .
لذلك أنا حر في القيام بما يلي: arr[0] = undefined .
إذا قمت بذلك ، فإن استخدام تلك الوظائف "الآمنة" _سيؤدي إلى إرجاع undefined للفتحة الأولى. لذلك في arr.forEach(x => ...) لا يمكنك قول ذلك x: T . لا يزال يتعين أن يكون x: T|undefined .

المثال الثاني سيهيئ كل قيمة إلى الافتراضي للمترجم لهذا النوع.

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

من الواضح أن TS لا يمكن أن تحميك من كل شيء في JS وهناك العديد من الوظائف / التركيبات التي يمكنك الاتصال بها من TS الصالحة ، والتي لها تأثيرات ديناميكية على نوع وقت تشغيل كائناتك وتكسر تحليل نوع TS الثابت.

هل سيكون سيئًا إذا كانت هذه فجوة في نظام الكتابة؟ أعني ، هذا الرمز ليس شائعًا حقًا: let x: number[] = []; x[3] = 0; وإذا كان هذا هو نوع الأشياء التي تريد القيام بها ، فربما يجب عليك التصريح عن المصفوفة الخاصة بك let x: number?[] .

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

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

بالنسبة لأولئك الذين لا يعرفون سبب أهمية هذا ، فهذا هو السبب نفسه
قد تريد فرقًا بين T و T|null .

في الساعة 9:11 صباحًا ، الجمعة ، 11 سبتمبر 2015 ، كتب jods [email protected] :

Griffork https://github.com/Griffork
لا أعرف ... يصبح الأمر محيرًا.

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

بسبب المراوغة في JS لا تعمل بهذه الطريقة. افترض اسمحوا arr:
(T | غير محدد) [].
لذلك أنا حر في القيام بما يلي: arr [0] = undefined.
إذا قمت بذلك ، فعندئذٍ باستخدام تلك الوظائف "الآمنة" _سيُرجع_غير محدد
للشق الأول. لذلك في arr.forEach (x => ...) لا يمكنك قول ذلك x: T.
لا يزال يتعين أن يكون x: T | غير محدد.

المثال الثاني سيهيئ كل قيمة إلى الافتراضي للمترجم
لهذا النوع.

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

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

هل سيكون سيئًا إذا كانت هذه فجوة في نظام الكتابة؟ أعني ، هذا الرمز
ليس شائعًا حقًا: دع x: رقم [] = []؛ س [3] = 0 ؛ وإذا كان هذا هو
نوع من الأشياء التي تريد القيام بها ثم ربما يجب عليك التصريح عن السماح للمصفوفة الخاصة بك
x: رقم؟ [].

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139408240
.

@ jods4 ما قصدته

jods أنا أشكو من JS ، وليس TS. سوف أعترف أنه خارج الموضوع.

في الخميس ، 10 سبتمبر 2015 ، الساعة 19:19 كتب Griffork [email protected] :

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

بالنسبة لأولئك الذين لا يعرفون سبب أهمية هذا ، فهذا هو السبب نفسه
قد تريد فرقًا بين T و T|null .

في الساعة 9:11 صباحًا ، الجمعة ، 11 سبتمبر 2015 ، كتب jods [email protected] :

Griffork https://github.com/Griffork
لا أعرف ... يصبح الأمر محيرًا.

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

بسبب المراوغة في JS لا تعمل بهذه الطريقة. افترض اسمحوا arr:
(T | غير محدد) [].
لذلك أنا حر في القيام بما يلي: arr [0] = undefined.
إذا قمت بذلك ، فعندئذٍ باستخدام تلك الوظائف "الآمنة" _سيُرجع_غير محدد
للشق الأول. لذلك في arr.forEach (x => ...) لا يمكنك قول ذلك x: T.
لا يزال يتعين أن يكون x: T | غير محدد.

المثال الثاني سيهيئ كل قيمة إلى الافتراضي للمترجم
لهذا النوع.

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

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

هل سيكون سيئًا إذا كانت هذه فجوة في نظام الكتابة؟ أعني ، هذا الرمز
ليس شائعًا حقًا: دع x: رقم [] = []؛ س [3] = 0 ؛ وإذا كان هذا هو
نوع من الأشياء التي تريد القيام بها ثم ربما يجب عليك التصريح عن مصفوفة الخاص بك
يترك
x: رقم؟ [].

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
<
https://github.com/Microsoft/TypeScript/issues/185#issuecomment-139408240>
.

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -139409349
.

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

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

(معظم هؤلاء الأشخاص ، من واقع خبرتي ، لم يسمعوا حتى بـ CoffeeScript ، ناهيك عن TypeScript. كثير منهم غير مدركين للإصدار الجديد من JS الذي تم الانتهاء منه للتو وتوحيده ، ES2015.)

هل هناك قرار ناشئ لهذا؟

نظرًا لعدم وجود نوع non-nullable ، لا يزال من المفيد أن تفشل TypeScript إذا حاول المرء الوصول إلى خاصية على متغير _assuredly_ null.

var o = null;
console.log(o.x);

... يجب أن تفشل.

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

يبدو أن هناك ثلاثة خيارات بافتراض أن تدقيق الحدود لا يتم فرضه على المصفوفات في وقت الترجمة:

  1. تعتبر فهرسة المصفوفة (وأي وصول عشوائي لعنصر مصفوفة بواسطة الفهرس) بمثابة إرجاع نوع فارغ ، حتى على مصفوفات من الأنواع غير القابلة للإلغاء. بشكل أساسي ، تحتوي "الطريقة" [] على نوع توقيع T? . إذا كنت تعلم أنك تقوم فقط بفهرسة التحقق من الحدود ، فيمكنك تحويل T? إلى T! في كود التطبيق الخاص بك.
  2. تُرجع فهرسة المصفوفة نفس النوع تمامًا (مع نفس القيمة الصفرية) مثل معلمة النوع العام للمصفوفة ، ويُفترض أن يتم فحص جميع وصول المصفوفة بواسطة التطبيق. سيؤدي الوصول خارج الحدود إلى إرجاع undefined ، ولن يتم اكتشافه بواسطة مدقق النوع.
  3. الخيار النووي: يتم تشفير جميع المصفوفات في اللغة على أنها لاغية ، وتفشل محاولات استخدام المصفوفات غير القابلة للإلغاء في عمليات التحقق من الكتابة.

كل هذه تنطبق أيضًا على الوصول المستند إلى الفهرس للخصائص الموجودة على الكائنات ، على سبيل المثال object['property'] حيث object من النوع { [ key: string ]: T! } .

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

هناك سؤال ثانٍ حول ما إذا كان يجب أن تكون الأنواع افتراضيًا غير قابلة للإلغاء أم أنها لاغية افتراضيًا. يبدو أنه في كلتا الحالتين سيكون من المفيد أن يكون لديك بناء جملة لكل من الأنواع القابلة للإلغاء صراحة وغير القابلة للإلغاء بشكل صريح من أجل التعامل مع الأدوية الجنيسة ؛ على سبيل المثال ، تخيل طريقة get في فئة حاوية (على سبيل المثال ، خريطة) تأخذ بعض القيمة وربما تُرجع نوعًا ، _ حتى إذا كانت الحاوية تحتوي فقط على قيم غير قابلة للإلغاء_:

class Container<K,V> {
  get(key: K): V? {
    // fetch from some internal data structure and return the value, if it exists
    // return null otherwise
  }
}

// only non-nullable values allowed in the container
const container = new Container<SomeKeyClass!, SomeValueClass!>();
const val: SomeValueClass!;
// ... later, we attempt to read from the container with a get() call
// even though only non-nullables are allowed in the container, the following should fail:
// get() explicitly returns null when the item can't be found
val = container.get(someKey);

وبالمثل ، قد نرغب (هذه حجة أقل قوة) في التأكد من أن فئة الحاوية الخاصة بنا لا تقبل سوى المفاتيح غير الفارغة في الإدخالات ، حتى عند استخدام نوع مفتاح nullable:

class Container<K, V> {
  insert(key: K!, val: V): void {
    // put the val in the data structure
    // the key must not be null here, even if K is elsewhere a nullable type
  }
}

const container = new Container<SomeKeyClass?, SomeValueClass>();
container.insert(null, new SomeValueClass()); // fails

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

عند النقطة التي يوجد فيها بناء جملة لكليهما ، يبدو أن الإعداد الافتراضي يمكن أن يكون علامة مترجم مشابه لـ --noImplicitAny . أنا شخصياً سأصوت للبقاء الافتراضي كما هو حتى الإصدار 2.0 ، ولكن يبدو أن كلاهما على ما يرام طالما أن هناك فتحة هروب (على الأقل مؤقتًا).

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

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

فيما يلي مقارنة مع كل خيار ، باستخدام دالة التجميع كمثال (العناصر الأولية هي الأكثر إشكالية):

// Option 1
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

// Option 2
function sum(numbers: !number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += numbers[i]
  }
  return res
}

// Option 3
function sum(numbers: number[]) {
  let res = 0
  for (let i = 0; i < numbers.length; i++) {
    res += <!number> numbers[i]
  }
  return res
}

مثال آخر: دالة map .

// Option 1
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(<!T> list[i], i));
  }
  return res
}

// Option 2
function map<T>(list: !T[], f: (value: !T, index: !number) => !T): !T[] {
  let res: !T[] = []
  for (let i = 0; i < list.length; i++) {
    res.push(f(list[i], i));
  }
  return res
}

// Option 3
function map<T>(list: T[], f: (value: !T, index: !number) => !T): T[] {
  let res: T[] = []
  for (let i = 0; i < list.length; i++) {
    const entry = list[i]
    if (entry !== undefined) {
      res.push(f(<!T> entry, i));
    }
  }
  return res
}

سؤال آخر: ما هو نوع entry في كل من هؤلاء؟ !string أو ?string أو string ؟

declare const regularStrings: string[];
declare const nullableStrings: ?string[];
declare const nonnullableStrings: !string[];

for (const entry of regularStrings) { /* ... */  }
for (const entry of nullableStrings) { /* ... */  }
for (const entry of nonnullableStrings) { /* ... */  }

كان الخيار الثالث عبارة عن اقتراح لسان الخد: Stuck_out_tongue:

رد: سؤالك الأخير:

declare const regularStrings: string[];
declare const nullableStrings: string?[];
declare const nonNullableStrings: string![]; // fails typecheck in option three

for(const entry of regularStrings) {
  // option 1: entry is of type string?
  // option 2: depends on default nullability
}

for(const entry of nullableStrings) {
  // option 1 and 2: entry is of type string?
}

for(const entry of nonNullableStrings) {
  // option 1: entry is of type string?
  // option 2: entry is of type string!
}

في بعض الحالات - حيث تريد إرجاع نوع غير قابل للإلغاء وتحصل عليه من مصفوفة ، على سبيل المثال - سيتعين عليك القيام بمهمة إضافية مع الخيار الأول بافتراض أنك في مكان آخر ضمنت عدم وجود قيم غير محددة في المصفوفة (لا تتغير متطلبات هذا الضمان بغض النظر عن الطريقة المتبعة ، فقط الحاجة إلى كتابة as string! ). أنا شخصياً ما زلت أفضلها لأنها أكثر وضوحًا (عليك أن تحدد متى تتخذ سلوكًا يحتمل أن يكون خطيرًا ، على عكس ما يحدث بشكل ضمني) وأكثر اتساقًا مع كيفية عمل معظم فئات الحاوية: على سبيل المثال ، الخريطة get الواضح أن الدالة Map.prototype.get خالية ، فمن المحتمل أن يفعل object['property'] نفس الشيء ، حيث أنها تقدم ضمانات مماثلة حول الإبطال وتستخدم بالمثل. وهو ما يترك المصفوفات مثل المصفوفات الفردية حيث يمكن أن تتسلل الأخطاء المرجعية الفارغة مرة أخرى ، وحيث يُسمح بالنفاذ العشوائي بأن يكون غير قابل للإلغاء بواسطة نظام الكتابة.

هناك بالتأكيد مناهج أخرى. على سبيل المثال ، يستخدم Flow حاليًا الخيار الثاني ، وقد قمت مؤخرًا بالتحقق من أن

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

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


يحتاج TypeScript حقًا إلى طريقة لتأكيد الأشياء ، نظرًا لأن التأكيدات غالبًا ما تكون كذلك
تستخدم للمساعدة في فحص النوع الثابت بلغات أخرى. لقد رأيت في
V8 كود قاعدة ماكرو UNREACHABLE(); يسمح للافتراضات بأن تكون a
بشكل أكثر أمانًا ، مما يؤدي إلى تعطل البرنامج في حالة انتهاك الثابت. سي ++
لديه static_assert للتأكيدات الثابتة للمساعدة في فحص النوع.

يوم الثلاثاء 20 أكتوبر 2015 الساعة 4:01 صباحًا ، مات بيكر إخطارات github.com
كتب:

كان الخيار الثالث عبارة عن اقتراح لسان الخد [صورة:
: stuck_out_tongue:]

رد: سؤالك الأخير:

التصريح عن const normalStrings: string [] ؛ إعلان const nullableStrings: string؟ [] ؛ إعلان const nonNullableStrings: string! []؛ // فشل كتابة الاختيار في الخيار الثالث
لـ (إدخال ثابت للسلاسل العادية) {
// الخيار 1: الإدخال من نوع سلسلة؟
// الخيار 2: يعتمد على الإلغاء الافتراضي
}
لـ (إدخال const من nullableStrings) {
// الخيار 1 و 2: الإدخال من نوع سلسلة؟
}
لـ (إدخال ثابت لـ nonNullableStrings) {
// الخيار 1: الإدخال من نوع سلسلة؟
// الخيار 2: الإدخال من نوع سلسلة!
}

في بعض الحالات - حيث تريد إرجاع نوع non-nullable وأنت
الحصول عليه من مصفوفة ، على سبيل المثال - سيتعين عليك القيام بتمثيل إضافي
مع الخيار الأول ، بافتراض أنك تضمن في مكان آخر عدم وجود ملفات غير محددة
القيم الموجودة في المصفوفة (متطلبات هذا الضمان لا تتغير
بغض النظر عن الطريقة التي يتم اتباعها ، فقط الحاجة إلى الكتابة كسلسلة!).
أنا شخصياً ما زلت أفضلها لأنها أكثر وضوحًا (عليك أن تفعل ذلك
تحديد متى تتخذ سلوكًا يحتمل أن يكون خطيرًا ، بدلاً من ذلك
يحدث ضمنيًا) وأكثر اتساقًا مع كيفية معظم فئات الحاويات
work: على سبيل المثال ، تقوم دالة get في الخريطة بإرجاع أنواع لاغية بوضوح
(يعيد الكائن إذا كان موجودًا تحت المفتاح ، أو فارغًا إذا لم يكن موجودًا) ،
وإذا قام Map.prototype.get بإرجاع قيمة خالية ، فإن الكائن ['خاصية']
ربما ينبغي أن تفعل الشيء نفسه ، لأنها تقدم ضمانات مماثلة حول
إبطال وتستخدم بالمثل. مما يترك المصفوفات على أنها فردية
حيث يمكن أن تتسلل الأخطاء المرجعية الفارغة مرة أخرى ، وحيث يكون الوصول العشوائي
يُسمح بأن يكون غير قابل للإلغاء بواسطة نظام النوع.

هناك بالتأكيد مناهج أخرى. على سبيل المثال ، يستخدم Flow حاليًا
الخيار الثاني http://flowtype.org/docs/nullable-types.html ، والأخير I
فحص SoundScript جعل المصفوفات المتفرقة غير قانونية بشكل صريح في مواصفاتها
https://github.com/rwaldron/tc39-notes/blob/master/es6/2015-01/JSExperimentalDirections.pdf
(حسنًا ، الوضع القوي / "SaneScript" يجعلها غير قانونية ، و SoundScript هو ملف
مجموعة شاملة من القواعد الجديدة) ، والتي تتجنب المشكلة إلى حد ما
على الرغم من أنهم سيظلون بحاجة إلى معرفة كيفية التعامل مع الطول اليدوي
التغييرات والتخصيص الأولي.

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -149468527
.

إيشيا ميدوز

هل يجب أن نبدأ في الاتصال بهم non-void الأنواع؟ على أي حال ، أعتقد أن تحديد non-void صراحة بـ T! أو !T هو خطأ. من الصعب أن تقرأ كإنسان ، ومن الصعب أيضًا التعامل مع جميع الحالات لمترجم TypeScript.

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

""
// وظيفة تجمع بسعادة
دالة لين (س: سلسلة): رقم {
العودة x.length ؛
}

لين ("الأعمال") ؛ // 5
لين (خالية) ؛ // خطأ ، لا يوجد طول خاصية فارغة

``` .ts
function len(x: string): number {
    if (x === null) {
        return -1;
    }
    return x.length;
}

len("works"); // 5
len(null); // null

ما سيحدث هنا حقًا هو نمذجة بيانات الإدخال كـ non-void ، لكن إضافة void ضمنيًا عند معالجتها في الوظيفة. وبالمثل ، فإن نوع الإرجاع هو non-void إلا إذا كان بإمكانه إرجاع null أو undefined

يمكننا أيضًا إضافة النوع ?T أو T? ، والذي يفرض الاختيار الفارغ (و / أو غير المحدد) قبل الاستخدام. أنا شخصياً أحب T? ، لكن هناك سابقة لاستخدام ?T مع Flow.

""
دالة لين (س:؟ سلسلة): رقم {
العودة x.length ؛ // خطأ: لا توجد خاصية طول على النوع؟ سلسلة ، يجب عليك استخدام نوع guard
}

One more example -- what about using function results?

``` .js
function len(x: string): number {
    return x.length;
}

function identity(f: string): string {
    return f;
}

function unknown(): string {
    if (Math.random() > 0.5) {
        return null;
    }
    return "maybe";
}

len("works"); // 5
len(null); // error, no property length of null

identity("works"); // "works": string
identity(null); // null: void
unknown(); // ?string

len(identity("works")); // 5
len(identity(null)); // error, no property length of null
len(unknown()); // error: no length property on type ?string, you must use a type guard

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

الجزء الصعب الوحيد هنا هو كيفية التعامل مع ملفات التعريف. أعتقد أن هذا يمكن حله من خلال افتراض أن ملف تعريف يعلن أن التحقق من function(t: T) لاغٍ / void ، مثل المثال الثاني. هذا يعني أن هذه الوظائف ستكون قادرة على أخذ قيم فارغة دون أن يولد المترجم خطأ.

هذا يسمح الآن لأمرين:

  1. الاعتماد التدريجي لبناء جملة النوع ?T ، والتي سيتم تبديل المعلمات الاختيارية لها بالفعل.
  2. في المستقبل ، يمكن إضافة علامة المترجم --noImplicitVoid والتي ستتعامل مع ملفات التصريح مثل ملفات التعليمات البرمجية المجمعة. سيكون هذا "كسرًا" ، ولكن إذا تم إجراؤه بعيدًا ، فستتبنى غالبية المكتبات أفضل ممارسة لاستخدام ?T عندما يكون النوع void و T عندما لا تستطيع. سيكون أيضًا قابلاً للتمكين ، لذلك سيتأثر فقط أولئك الذين يختارون استخدامه. قد يتطلب هذا أيضًا استخدام بناء الجملة ?T في الحالة التي يمكن أن يكون فيها الكائن فارغًا.

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

البادئة ? تستخدم أيضًا في التعليقات التوضيحية لمجمع الإغلاق ، IIRC.

في الثلاثاء ، 17 نوفمبر 2015 ، 13:37 كتب Tom Jacques [email protected] :

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

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

// function compiles happilyfunction len (x: string): number {
العودة x.length ؛
}

لين ("الأعمال") ؛ // 5
لين (خالية) ؛ // خطأ ، لا يوجد طول خاصية فارغة

دالة لين (س: سلسلة): رقم {
إذا (س === خالية) {
عودة -1 ؛
}
العودة x.length ؛
}

لين ("الأعمال") ؛ // 5
لين (خالية) ؛ // باطل

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

يمكننا أيضا إضافة؟ T أو T؟ type ، مما يفرض القيمة الفارغة (و / أو
undefined) تحقق قبل الاستخدام. أنا شخصياً أحب T؟ ولكن هناك سابقة
لاستخدام؟ T مع التدفق.

دالة لين (س:؟ سلسلة): رقم {
العودة x.length ؛ // خطأ: لا توجد خاصية طول على النوع؟ سلسلة ، يجب عليك استخدام نوع guard
}

مثال آخر - ماذا عن استخدام نتائج الوظيفة؟

دالة لين (س: سلسلة): رقم {
العودة x.length ؛
}
هوية الوظيفة (f: string): string {
العودة و ؛
}
دالة غير معروفة (): سلسلة {
إذا (Math.random ()> 0.5) {
عودة فارغة ؛
}
العودة "ربما" ؛
}

لين ("الأعمال") ؛ // 5
لين (خالية) ؛ // خطأ ، لا يوجد طول خاصية فارغة

الهوية ("الأعمال") ؛ // "يعمل": سلسلة
هوية (خالية) ؛ // الفراغ باطل
غير معروف()؛ // ؟سلسلة

لين (الهوية ("الأعمال")) ؛ // 5
لين (هوية (خالية)) ؛ // خطأ ، لا يوجد طول خاصية فارغة
لين (غير معروف ()) ؛ // خطأ: لا توجد خاصية طول على النوع؟ سلسلة ، يجب عليك استخدام نوع guard

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

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

هذا يسمح الآن لأمرين:

  1. اعتماد تدريجي للنحو نوع T ، والتي اختيارية
    سيتم بالفعل تبديل المعلمات إلى.
  2. في المستقبل يمكن إضافة علامة مترجم - noImplicitVoid
    والتي من شأنها أن تتعامل مع ملفات التصريح مثل ملفات التعليمات البرمجية المجمعة. هذه
    سيكون "كسرًا" ، ولكن إذا تم إجراؤه بعيدًا ، فإن غالبية
    المكتبات ستتبنى أفضل ممارسة لاستخدام؟ T عندما يمكن للنوع
    تكون باطلة و T عندما لا تستطيع ذلك. سيكون أيضًا اختيارًا ، لذلك فقط هؤلاء
    الذين يختارون استخدامه سيتأثرون.

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157463734
.

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

""
واجهة withOptionalProperty {
س ؟: سلسلة
}
واجهة withVoidableProperty {
س:؟ سلسلة
}

الدالة withOptionalParam (o ؟: string) {}
الدالة withVoidableParam (o:؟ string) {}
""

في الواقع ، يستخدمون البادئة . يستخدمون ? للأنواع الفارغة الصريحة و ! للأنواع غير القابلة للإلغاء ، مع كون القيمة nullable هي القيمة الافتراضية.

التمييز الذي لا يمكن تجنبه مقابل لاغى له معنى كبير. : +1:

أشعر أن هذا يدور في دوائر.

هل قرأت جميعًا أعلاه لماذا لن يحل التعريف الذي لا يمكن إلغاؤه / غير القابل للإبطال المشكلة الأساسية؟

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

""
دالة لين (س: سلسلة): رقم {
العودة x.length ؛
}

لين ("الأعمال") ؛
// لا يوجد خطأ - 5

لين (خالية) ؛
// يسمح المترجم بهذا ولكن يجب أن يخطئ هنا بشيء مثل
// خطأ: لا توجد خاصية "طول" خالية

لين (غير محدد) ؛
// يسمح المترجم بهذا ولكن يجب أن يخطئ هنا بشيء مثل
// خطأ: لا توجد خاصية "طول" غير محددة
""

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

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

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

الجزء الأول:

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

الجزء الثاني:

  • انتظر. لاحقًا ، بعد تقديم الجزء الأول ، وكان لدى مجتمع ومستخدمي TS الوقت لاستخدام هذه الميزات ، سيكون المعيار هو استخدام T عندما لا يكون النوع فارغًا ، و ?T عندما يكون نوع يمكن أن تكون فارغة. هذا ليس مضمونًا ، لكنني أعتقد أن هذا سيكون أفضل ممارسة واضحة وواضحة.
  • كميزة منفصلة ، أضف خيار المترجم --noImplicitVoid الذي يتطلب أن تكون الأنواع ?T إذا كان من الممكن أن تكون باطلة. هذا هو مجرد المترجم الذي يطبق أفضل الممارسات الموجودة بالفعل. إذا كان هناك ملف تعريف لا يلتزم بأفضل الممارسات ، فسيكون غير صحيح ، ولكن هذا هو سبب الاشتراك.
  • إذا كنت تريد حقًا أن تكون صارمًا ، فقد تقبل العلامة الوسائط التي تحدد الأدلة / الملفات التي يجب أن تنطبق عليها. ثم يمكنك تطبيق التغيير على الكود الخاص بك فقط ، واستبعاد node_modules .

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

tejacques أنا ، على سبيل موافق تمامًا على هذا.

tejacques - FWIW ، أتفق تمامًا مع

في الواقع ، هناك شيئان:

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

أود أيضًا أن أقوم بعبارة --noImplicitVoid بشكل مختلف قليلاً - لنفترض أنه لا يسمح بتعيين أنواع null و undefined لأنواع غير قابلة للإبطال (أي افتراضي) بدلاً من "تتطلب الأنواع لتكون كذلك ?T إذا كان من الممكن أن تكون باطلة ". أنا متأكد من أننا نعني نفس الشيء ، مجرد دلالات ؛ تركز على الاستخدام بدلاً من التعريف ، والتي ، إذا فهمت كيفية عمل TS ، فهي الأشياء الوحيدة التي يمكنها فعلاً فرضها.

وقد خطر ببالنا شيء الآن: سيكون لدينا بعد ذلك أربعة مستويات من الإبطال (وهذا ينطبق أيضًا على Flow ، والذي أعتقد أنه يلهم قدرًا كبيرًا من هذه المحادثة):

interface Foo {
  w: string;
  x?: string;
  y: ?string;
  z?: ?string;
}

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

x متساهل تمامًا في ظل الإعدادات الحالية. يمكن أن تكون سلسلة ، فارغة ، غير محددة ، ولا يلزم حتى أن تكون موجودة في الكائنات التي تنفذها. تحت --noImplicitVoid ، تصبح الأمور أكثر تعقيدًا قليلاً ... قد لا تحددها ، لكن إذا قمت بتعيين شيء ما لها ، ألا يمكن أن تكون باطلة؟ أعتقد أن الطريقة التي يتعامل بها Flow مع هذا الأمر هي أنه يمكنك تعيين x إلى undefined (لتقليد عدم الوجود) ، لكن ليس null . قد يكون هذا قليلًا بعض الشيء بالنسبة لـ TypeScript ، على الرغم من ذلك.

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

يمكن تعيين y على أي سلسلة أو قيمة باطلة ، ولكن _يجب_ أن تكون موجودة في شكل ما ، حتى لو كانت باطلة. وبالطبع ، يتطلب الوصول إليه فحصًا باطلًا. قد يكون هذا السلوك مفاجئًا بعض الشيء. هل يجب أن نعتبر _not_ تعيينه هو نفسه تعيينه على undefined ؟ إذا كان الأمر كذلك ، فما الذي يجعله مختلفًا عن x ، باستثناء طلب شيك باطل؟

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

هناك قليلا من التداخل بين x و y ، وأظن أن x في نهاية المطاف تصبح مهملة من قبل المجتمع، مفضلا z شكل لأقصى درجات السلامة .

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

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

هذا البيان غير صحيح. لا يمكن حساب الكثير من جوانب الإلغاء حتى مع توفر مصدر الكود الكامل.

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

مثال آخر: إذا كان لدي مصفوفة list ووظيفة unsafe(x) يظهر كود مصدرها أنها لا تقبل وسيطة null ، لا يمكن للمجمع أن يقول ما إذا كان ذلك الخط آمن أم لا: list.filter(unsafe) . وفي الواقع ، ما لم تكن تعرف بشكل ثابت ما ستكون عليه جميع المحتويات الممكنة لـ list ، فلن يمكن القيام بذلك.

حالات أخرى مرتبطة بالميراث وأكثر.

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

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

tejacques سيئي ، لقد أخطأت في قراءة تعليقك (لسبب ما قرر عقلي void===null :( ألقي باللوم على الاستيقاظ للتو).
شكرًا على المنشور الإضافي ، مع ذلك ، فقد كان منطقيًا بالنسبة لي أكثر من مشاركتك الأصلية. أنا في الواقع أحب هذه الفكرة تمامًا.

سأقوم بالرد على ثلاثة منكم كل على حدة لأن كتابته على منشور واحد هو نقطة غير قابلة للقراءة.

تضمين التغريدة أحيانًا يكون من الصعب نقل كل شيء بشكل صحيح من خلال النص وأعتقد أن الأمر يستحق التوضيح على أي حال.

dallonf إعادة

أعتقد أن الفرق بين x?: T و y: ?T سيكون في استخدام تلميحات الأدوات / الوظيفة ، وفي الكتابة والحماية المستخدمة.

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

w: T هو وسيط مطلوب بخلاف void (مع --noImplicitVoid )
لا يحتاج الى حارس

x?: T وسيطة اختيارية ، لذا فالنوع هو بالفعل T | undefined
يتطلب if (typeof x !== 'undefined') حارس.
لاحظ الصورة الرمزية الثلاثية !== للتحقق الدقيق من undefined .

y: ?T وسيطة مطلوبة ، والنوع هو بالفعل T | void
يتطلب if (y == null) حارس.
لاحظ الحرف الرسومي المزدوج == الذي يتطابق مع كل من null و undefined ie void

z?: ?T وسيطة اختيارية ، والنوع هو بالفعل T | undefined | void وهو T | void
يتطلب if (z == null) حارس.
لاحظ مرة أخرى الحرف الرسومي المزدوج == الذي يطابق كلاً من null و undefined ie void

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

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

تحرير: صياغة محدثة وإصلاح بعض الأخطاء المطبعية.

@ jods4 أتفق مع كل ما قلته إلى حد كبير. لا أحاول التقليل من أهمية الكتابة غير void . أنا فقط أحاول دفعها مرحلة واحدة في كل مرة. إذا لم يتمكن فريق TS من القيام بذلك لاحقًا ، فنحن على الأقل أفضل حالًا ، وإذا كان بإمكانهم القيام بذلك على الطريق بعد تنفيذ المزيد من الشيكات و ?T فإن هذه المهمة قد أنجزت.

أعتقد أن قضية Arrays صعبة حقًا. يمكنك دائمًا فعل شيء مثل هذا:

""
الدالة numToString (x: number) {
إرجاع x.toString () ؛
}
var nums: number [] = Array (100) ؛
numToString (أرقام [0]) ، // أنت أبله!

You can try to do something specifically for uninitialized arrays, like typing the `Array` function as `Array<?T>` / `?T[]` and upgrading it to `T[]` after a for-loop initializing it, but I agree that you can't catch everything. That said, that's already a problem anyway, and arrays typically don't even send uninitialized values to `map`/`filter`/`forEach`.

Here's an example -- the output is the same on Node/Chrome/IE/FF/Safari.

``` .ts
function timesTwo(x: number) {
    return x * 2;
}
function all(x) {
    return true;
}
var nums: number[] = Array(100);
nums.map(timesTwo);
// [undefined x 100]
nums.filter(all);
// []
nums.forEach(function(x) { console.log(x); })
// No output

هذا في الحقيقة لا يساعدك كثيرًا ، لأنه غير متوقع ، لكنه ليس خطأً في JavaScript حقيقي اليوم.

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

هنا مثال. لنفترض أن --noImplicitVoid متوقف

""
واجهة ITransform{
(x: T): U ؛
}

واجهة IHaveName {
الاسم: سلسلة ؛
}

تحويل وظيفة(x: T، fn: ITransform) {
عودة fn (x) ؛
}

فار اسمه = {
الاسم: "فو"
} ؛

var wrongName = {
الاسم: 1234
} ؛

var اسمهNull = {
الاسم: null
} ؛

var someFun = (x: IHaveName) => x.name ؛
var someFunHandlesVoid = (x: IHaveName) => {
إذا (x! = null && x.name! = null) {
إرجاع x.name ؛
}
إرجاع "بلا اسم" ؛
} ؛

All of the above code compiles just fine -- no issues. Now let's try using it

``` .ts
someFun(named);
// "Foo"
someFun(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'number' is not assignable to type 'string'.
someFun(null);
// Not currently an error, but would be something like this:
// error TS#: Argument of type 'null' is not assignale to parameter of type 'IHaveName'.
someFun(namedNull);
// Not currently an error, but would be something like this:
// error TS#: Argument of type '{ name: null; }' is not assignable to parameter of
// type 'IHaveName'.
//   Types of property 'name' are incompatible.
//     Type 'null' is not assignable to type 'string'.

someFunHandlesVoid(named);
// "Foo"
someFunHandlesVoid(wrongName);
// error TS2345: Argument of type '{ name: number; }' is not assignable to parameter
// of type 'IHaveName'.
someFunHandlesVoid(null);
// "No Name"
someFunHandlesVoid(namedNull);
// "No Name"

transform(named, someFun);
// "Foo"
transform(wrongName, someFun);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'number'.
transform(null, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate 'null' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(namedNull, someFun);
// Not currently an error, but would be something like this:
// error TS#: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: null; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
//     Types of property 'name' are incompatible.
//       Type 'string' is not assignable to type 'null'.

transform(named, someFunHandlesVoid);
// "Foo"
transform(wrongName, someFunHandlesVoid);
// error TS2453: The type argument for type parameter 'T' cannot be inferred from the usage.
// Consider specifying the type arguments explicitly.
//   Type argument candidate '{ name: number; }' is not a valid type argument because it
//   is not a supertype of candidate 'IHaveName'.
transform(null, someFunHandlesVoid);
// "No Name"
transform(namedNull, someFunHandlesVoid);
// "No Name"

أنت محق في أنك لا تستطيع التقاط كل شيء ، لكن يمكنك التقاط الكثير من الأشياء.

ملاحظة أخيرة - ماذا يجب أن يكون السلوك المذكور أعلاه عند تشغيل --noImplicitVoid ؟

الآن يتم فحص كل من someFun و someFunHandlesVoid بالطريقة نفسها وينتجان نفس رسائل الخطأ التي أنتجها someFun . على الرغم من أن someFunHandlesVoid يتعامل مع الفراغ ، فإن استدعائه بـ null أو undefined هو خطأ لأن التوقيع ينص على أنه لا يأخذ void . يجب كتابته على النحو التالي (x: ?IHaveName) : string لقبول null أو undefined . إذا قمنا بتغيير نوعه ، فسيستمر في العمل كما كان من قبل.

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

أشعر أنني غبي تمامًا لفعل هذا ، لكنني سأقوم بعمل منشور آخر.

في هذه المرحلة ، لست متأكدًا مما يجب فعله للمتابعة. هل هناك فكرة أفضل؟ هل ينبغي لنا:

  • استمر في المناقشة / تحديد كيف يجب أن يتصرف هذا؟
  • تحويل هذا إلى ثلاثة مقترحات ميزة جديدة؟

    • التحليل المحسن

    • ربما / الخيار اكتب ?T

    • خيار المترجم --noImplicitVoid

  • بينغ أعضاء فريق TypeScript للإدخال؟

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

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

  • تفتقد typeof في المثلث يساوي dallonf.
  • يبدو أنك تفتقد بعض ? في المثال لـ jods4.

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

تحرير: الشطب المتذكر موجود.

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

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

+1 لتقسيم هذا ، ولكن في الوقت الحالي ، يمكن للخيار --noImplicitVoid القيام بذلك
انتظر حتى يتم تنفيذ النوع nullable.

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

في الأربعاء ، 18 نوفمبر 2015 ، الساعة 21:24 كتب Tom Jacques [email protected] :

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

في هذه المرحلة ، لست متأكدًا مما يجب فعله للمتابعة. هل هناك فكرة أفضل؟
هل ينبغي لنا:

  • استمر في المناقشة / تحديد كيف يجب أن يتصرف هذا؟
  • تحويل هذا إلى ثلاثة مقترحات ميزة جديدة؟

    • التحليل المحسن

    • ربما / نوع الخيار؟

    • - noImplicitVoid خيار المترجم

  • بينغ أعضاء فريق TypeScript للإدخال؟

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

-
قم بالرد على هذا البريد الإلكتروني مباشرة أو قم بعرضه على GitHub
https://github.com/Microsoft/TypeScript/issues/185#issuecomment -157928828
.

+1 لخيار --noImplicitNull (عدم السماح بالتخصيص الباطل والباطل).

لقد حاولت التخفيف من هذه المشكلة بنوع خاص Op<A> = A | NullType . ويبدو أن تعمل بشكل جيد جدا. انظر هنا .

+1 لـ _-- noImplicitNull_ كذلك يرجى: +1:

+1 لـ - noImplicitNull

هل يجب إغلاق هذا؟

تم دمجGaelan Given # 7140 ، إذا كنت ترغب في تقديم إصدار جديد ومخصص --noImplicitNull كما اقترحه عدد قليل من الأشخاص هنا ، فمن المحتمل أن يكون القيام بذلك آمنًا الآن.

isiahmeadows ربما يكون من الأفضل ترك هذا مفتوحا بعد ذلك.

هل يجب إغلاق هذا؟

نعتقد أن https://github.com/Microsoft/TypeScript/issues/2388 هو جزء إعادة التسمية من هذا العمل. هذا هو السبب في أننا لم نعلن عن اكتمال هذه الميزة حتى الآن.

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

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

mhegazy كانت الفكرة التي تم طرحها سابقًا في هذا العدد مقابل --noImplicitNull هي أن كل شيء يجب أن يكون صراحةً ?Type أو !Type . IMHO لا أشعر أنه يستحق الأمر المعياري عندما يكون هناك علم آخر يستنتج غير قابل للإلغاء افتراضيًا أن IIRC تم تنفيذه بالفعل عندما كانت الأنواع القابلة للإلغاء نفسها.

سيتم الإغلاق الآن بعد دمج كل من # 7140 و # 8010.

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

@ massimiliano-mantione ، يُرجى مراجعة https://github.com/Microsoft/TypeScript/issues/8405

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

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

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

kyasbal-1994 picture kyasbal-1994  ·  3تعليقات

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

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

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