circumspect
عبارة عن مجموعة من الوظائف لجعل كود TypeScript/JavaScript الخاص بك أكثر أمانًا. تتضمن هذه الوظائف invariant
و warning
و assertNever
والمزيد.
هناك العديد من الوظائف المفيدة التي يمكن أن تجعل التعليمات البرمجية الخاصة بك أكثر أمانًا، مثل invariant
و assertNever
. تتوفر العديد من هذه الوظائف كحزم npm منفصلة (على سبيل المثال، invariant
). لكن تثبيت حزمة جديدة (وفي معظم الأحيان نظيرة @types
أيضًا) لكل وظيفة ليس أمرًا مريحًا للغاية. أيضًا، تقوم هذه الحزم عمومًا بتصدير وظائفها كصادرات افتراضية، مما يجعل الاستيراد التلقائي لـ VSCode لا يعمل بشكل جيد معها.
تعمل هذه المكتبة على توحيد الوظائف التي تجعل التعليمات البرمجية الخاصة بك أكثر أمانًا في مكان واحد. circumspect
هو تبعية واحدة تحتوي عليهم جميعًا. ويعمل الاستيراد التلقائي لـ VSCode كما هو متوقع لأن جميع الوظائف التي يوفرها circumspect
تتم تسميتها بالصادرات.
علاوة على ذلك، فإن circumspect
مكتوب بنسبة 100% في TypeScript، لذلك تتم كتابة كل وظيفة بشكل صحيح افتراضيًا. لذا، ليست هناك حاجة لتثبيت حزم @types
منفصلة.
باستخدام الغزل:
yarn add circumspect
باستخدام npm:
npm install circumspect
invariant
warning
assertNever
nonNull
invariant
invariant ( value : unknown , message ?: string ) : asserts value
value: unknown
هي القيمة التي نريد التأكد من صحتها.
message?: string
هي رسالة خطأ اختيارية سيتم تضمينها في الخطأ الذي يتم طرحه عندما تكون value
التي تم تمريرها خاطئة. يتم عرض رسالة الخطأ المخصصة هذه فقط في التطوير. في الإنتاج، يظهر خطأ عام بدلاً من ذلك ( 'Invariant violation'
) ويتم تجاهل معلمة message
.
asserts value
مما يعني أن الوظيفة تقوم ببساطة بتضييق نوع value
لتكون صادقة ولا تُرجع أي شيء. يضمن invariant
أن value
المحددة صحيحة. إذا لم يكن الأمر كذلك، فإنه يلقي خطأ يحتوي على message
المحددة (في التطوير فقط). تعمل هذه الوظيفة على تضييق نوع value
بشكل صحيح عن طريق استبعاد كافة القيم الزائفة (على سبيل المثال، null
و undefined
).
يجب استخدام invariant
عندما تكون لدينا قيمة من المحتمل أن تكون خاطئة، ولكننا واثقون من أنه في ظروف معينة، يجب أن تكون القيمة صادقة. أي أن كون القيمة صادقة هو أمر ثابت، وإذا حدث أن القيمة كاذبة، فقد تم انتهاك الثوابت، وبالتالي لا بد من إلقاء الخطأ.
نظرًا لأنه يتم تجاهل وسيطة message
تمامًا في الإنتاج، فقد ترغب في إزالتها من التعليمات البرمجية الخاصة بك تمامًا في إصدارات الإنتاج. لمعرفة كيفية القيام بذلك، راجع قسم التحسينات.
declare const user : User | null | undefined ;
invariant ( user , 'The user is missing!' ) ;
user ; // If we get here, the type is narrowed to `User`
في هذا المثال، من المحتمل أن يكون كائن user
null
أو undefined
(أي خطأ). إذا كان الأمر كذلك، فلدينا انتهاك ثابت، وسيتم طرح الدالة invariant
. بخلاف ذلك، فإنه يعود ببساطة ونعلم أن user
يشير فعليًا إلى كائن المستخدم.
warning
warning ( value : unknown , message : string ) : void
value: unknown
هي القيمة التي نريد التحقق منها. إذا كان خطأ، يتم إصدار تحذير إلى وحدة التحكم.
message: string
هي رسالة التحذير التي سيتم كتابتها إلى وحدة التحكم.
void
الوظيفة لا ترجع أي شيء. warning
يصدر تحذيرًا لوحدة التحكم message
المحددة إذا كانت value
المحددة خاطئة. يتم إصدار التحذير فقط في التنمية. في الإنتاج، هذه الوظيفة لا تفعل أي شيء.
يجب استخدامه عندما نريد إصدار تحذير للتطوير فقط إذا كانت بعض القيمة خاطئة، مما يساعد في توجيه المطورين لإصلاح المشكلات غير الحرجة التي يتم الإبلاغ عنها في رسالة التحذير.
نظرًا لأن warning
لا يفعل أي شيء في الإنتاج، فقد ترغب في إزالة استدعاءات هذه الوظيفة بالكامل من التعليمات البرمجية الخاصة بك في إصدارات الإنتاج. لمعرفة كيفية القيام بذلك، راجع قسم التحسينات.
declare const mode : 'auto' | 'default' | 'slow' | 'fast' ;
warning (
mode !== 'auto' ,
'Mode "auto" has been deprecated. Please use "default" instead.' ,
) ;
في هذا المثال، نريد إصدار تحذير إهمال إذا كان mode
'auto'
. للقيام بذلك، نحتاج إلى تمرير قيمة خاطئة، ولهذا السبب نقوم بتمرير mode !== 'auto'
، وهو خطأ فقط عندما يكون mode
'auto'
.
في بعض الحالات، يكون من المنطقي تمرير false
بشكل مباشر. على سبيل المثال:
declare const languages : Language [ ] ;
declare const defaultLanguage : Language ;
declare const langName : string ;
let lang = languages . find ( ( { name } ) => name === langName ) ;
if ( ! lang ) {
warning (
false ,
`Language with name " ${ langName } " not found. Falling back to the default language.` ,
) ;
lang = defaultLanguage ;
}
assertNever
assertNever ( value : never ) : never
value: never
بعد استنفاد جميع متغيرات نوع الاتحاد الممكنة. never
مما يعني أن الدالة لا تعود أبدًا؛ إنه يلقي خطأً فقط إذا تم استدعاؤه بالفعل. يجب استخدام assertNever
للتأكد من استنفاد كافة متغيرات نوع الاتحاد.
إذا تم استنفاد جميع المتغيرات الموحدة value
، فلن يكون هناك خطأ في المحول البرمجي عند استدعاء assertNever
مع value
، نظرًا لأن value
تعتبر من النوع never
في تلك المرحلة، وفي وقت التشغيل، لن نصل أبدًا إلى نقطة استدعاء assertNever
، مما يعني أن لن يتم طرح أي خطأ.
ومع ذلك، إذا لم يتم استنفاد جميع متغيرات الاتحاد، فإننا نستدعي assertNever
بشيء آخر بدلاً من never
وبالتالي سيكون هناك خطأ في المترجم يقول شيئًا مثل
Argument of type 'x' is not assignable to parameter of type 'never'.
والتي يمكننا إصلاحها من خلال التعامل مع المتغيرات المفقودة. يمكنك قراءة المزيد حول التحقق من شمولية الاتحاد والتأكيد assertNever
في مستندات TypeScript.
declare const state : 'loading' | 'done' | 'error' ;
switch ( state ) {
case 'loading' :
return < Loading / > ;
case 'done' :
return < Done / > ;
case 'error' :
return < Error / > ;
}
في هذا المثال، سنتعامل مع جميع الحالات الممكنة داخل عبارة switch
: 'loading'
، 'done'
، و 'error'
.
ومع ذلك، ماذا لو أضفنا حالة أخرى في المستقبل، مثل 'pending'
؟
حقيقة أن عبارة switch
لا تتعامل مع 'pending'
لن يتم اكتشافها.
الحل هو أن يكون لدينا حالة default
نؤكد فيها أنه قد تم التعامل مع جميع الحالات المحتملة.
switch ( state ) {
...
default :
return assertNever ( state ) ;
}
لذلك، عندما تتم معالجة جميع متغيرات الحالة، لا نحصل على أي خطأ في وقت الترجمة. ومع ذلك، عندما نضيف الحالة 'pending'
الجديدة، سنحصل على خطأ في المترجم يقول:
Argument of type 'string' is not assignable to parameter of type 'never'.
يمكننا إصلاح هذا الخطأ عن طريق التعامل مع الحالة 'pending'
داخل switch
.
كما ترون من هذا المثال، فإن assertNever
مفيدة بشكل خاص في عبارات switch
حيث نريد التأكد من التعامل مع جميع الحالات المحتملة في جميع الأوقات.
nonNull
nonNull < T > ( value : T | null | undefined ) : value is T
value: T | null | undefined
هي القيمة التي نريد التحقق من كونها null
أو undefined
. value is T
مما يعني أن الدالة تُرجع قيمة منطقية تشير إلى ما إذا كانت القيمة التي تم تمريرها ليست null
أو undefined
. يؤدي هذا إلى تضييق نوع value
إلى T
(على سبيل المثال، استبعاد null
undefined
) عند إرجاع true
. nonNull
هي دالة أصلية تتحقق مما إذا كانت القيمة المحددة غير فارغة، أي ليست null
أو undefined
. بعد استدعاء الدالة، يتم تضييق نوع value
بشكل صحيح بناءً على ما إذا كان تم إرجاع true
أم false
.
يجب استخدام nonNull
عندما يكون لدينا قيمة من المحتمل أن تكون null
أو undefined
أو كليهما ونريد التحقق من ذلك وتضييق نوعه بشكل صحيح.
ينبع اسم هذه الوظيفة من نوع الأداة المساعدة NonNullable
، الذي يستبعد null
undefined
من النوع.
declare const names : ( string | null ) [ ] ;
const nonNullNames = names . filter ( nonNull ) ;
// nonNullNames has type 'string[]'
في هذا المثال، لدينا مصفوفة من الأسماء التي يمكن أن تتضمن أيضًا بعض العناصر null
. نقوم بتصفية المصفوفة لجميع العناصر غير الفارغة ونستعيد string[]
.
إذا استخدمنا names.filter(x => x !== null)
بدلاً من ذلك، فسنستعيد العناصر غير الخالية، لكن النوع سيظل (string | null)[]
نظرًا لأن TypeScript ترى الوظيفة التي نمررها filter
كمجرد إرجاع boolean
وبالتالي لا يحدث أي تضييق للنوع.
نوصي باستخدام babel-plugin-dev-expression
لتجريد وسيطة message
التي تم تمريرها إلى invariant
ولإزالة استدعاءات warning
تمامًا في الإنتاج.
في الأساس، في الإنتاج، يتم استبدال babel-plugin-dev-expression
invariant ( value , 'Value is falsy!' ) ;
مع
if ( ! value ) {
invariant ( false ) ;
}
لذلك يتم تجريد وسيطة message
. كما أنه يزيل تمامًا مكالمات warning
. لذلك، خطوط مثل هذا
warning ( value , 'Value is falsy!' ) ;
تتم إزالتها.
طلبات السحب هي موضع ترحيب كبير. إذا كنت تنوي إدخال تغيير كبير، يرجى فتح موضوع ذي صلة أولاً حيث يمكننا مناقشة ما تريد تغييره.
يرجى التأكد من تحديث الاختبارات والملف التمهيدي (README) بالشكل المناسب.
معهد ماساتشوستس للتكنولوجيا