نسخة مطبوعة على الآلة الكاتبة. تعبير السحر

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

قليلا من الحقيقة


صحيح N1.

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

صحيح N2.

القيمة العملية لبعض التصاميم هي 0.

صحيح N3.

تم اختبار الأمثلة تحت tsc الإصدار 3.4.5 والهدف es5. فقط في حالة ، تحت التكوين المفسد

tsconfig.json
{
"CompilerOptions": {
"OutFile": "./target/result.js" ،
"الوحدة النمطية": "amd" ،
"الهدف": "es5" ،
"إعلان": صحيح ،
"NoImplicitAny": صحيح ،
"NoImplicitReturns": صحيح ،
"StrictNullChecks": صحيح ،
"StrictPropertyInitialization": صحيح ،
"الديكور التجريبي": صحيح ،
"EmitDecoratorMetadata": صحيح ،
"PreserveConstEnums": صحيح ،
"NoResolve": صحيح ،
"SourceMap": صحيح ،
"InlineSources": صحيح
}
"تشمل": [
"./Src"
]
}


التنفيذ والميراث


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

abstract class ClassA { abstract getA(): string; } abstract class ClassB { abstract getB(): string; } // , tsc     abstract class ClassC implements ClassA, ClassB { // ^  ,   implements  . abstract getA(): string; abstract getB(): string; } 

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

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

 class One { one = "__one__"; getOne(): string { return "one"; } } class Two { two = "__two__"; getTwo(): string { return "two"; } } //  ,    :   IDE (  )    . class BothTogether extends mix(One, Two) { // ^   ,    extends   info(): string { return "BothTogether: " + this.getOne() + " and " + this.getTwo() + ", one: " + this.one + ", two: " + this.two; // ^   IDE   ^  } } type FaceType<T> = { [K in keyof T]: T[K]; }; type Constructor<T> = { // prototype: T & {[key: string]: any}; new(): T; }; // TODO:    ,   .       function mix<O, T, Mix = O & T>(o: Constructor<O>, t: Constructor<T>): FaceType<Mix> & Constructor<Mix> { function MixinClass(...args: any) { o.apply(this, args); t.apply(this, args); } const ignoreNamesFilter = (name: string) => ["constructor"].indexOf(name) === -1; [o, t].forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).filter(ignoreNamesFilter).forEach(name => { MixinClass.prototype[name] = baseCtor.prototype[name]; }); }); return MixinClass as any; } const bt = new BothTogether(); window.console.log(bt.info()); // >> BothTogether: one and two, one: __one__, two: __two__ 

البحث : عميق وفي نفس الوقت مجهول الهوية.

 const lass = class extends class extends class extends class extends class {} {} {} {} {}; 

ومن سيكتب فئة الكلمة مع 4 يمتد في المثال أعلاه؟

إذا كان الأمر كذلك
 // tslint:disable const Class = class Class extends class Class extends class Class extends class Class extends class Class {} {} {} {} {}; 

و اكثر؟

مثل هذا
 // tslint:disable const lass = class Class<Class> extends class Class extends class Class extends class Class extends class Class {} {} {} {} {}; 

حسنًا ، يمكنك الحصول عليها - إنها مجرد صف!

علامة تعجب - مشغل غير محدود ومعدل



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

بحث : علامة تعجب كمشغل للتأكيد بدون قيمة

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

  //     --strictNullChecks type OptType = { maybe?: { data: string; }; }; // ... function process(optType: OptType) { completeOptFields(optType); //   ,   completeOptFields    . window.console.log(optType.maybe!.data); // ^ -    ,    null //   !,    tsc: Object is possibly 'undefined' } function completeOptFields(optType: OptType) { if (!optType.maybe) { optType.maybe = { data: "some default info" }; } } 

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

بحث : علامة التعجب كمعدل تأكيد التوكيد المحدد

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

 //     --strictPropertyInitialization class Field { foo!: number; // ^ // Notice this '!' modifier. // This is the "definite assignment assertion" constructor() { this.initialize(); } initialize() { this.foo = 0; // ^   } } 

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

سؤال: هل تعتقد أن العبارة التالية سوف تجمع؟

 //     --strictNullChecks type OptType = { maybe?: { data: string; }; }; function process(optType: OptType) { if (!!!optType.maybe!!!) { window.console.log("Just for fun"); } window.console.log(optType.maybe!!!!.data); } 

الجواب
نعم

أنواع



يكتشف كل من يكتب أنواعًا معقدة الكثير من الأشياء المثيرة للاهتمام. لذا فقد حالفني الحظ.

بحث : يمكن الرجوع إلى نوع فرعي باسم حقل من النوع الرئيسي.

 type Person = { id: string; name: string; address: { city: string; street: string; house: string; } }; type Address = Person["address"]; 

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

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

 class BaseDialog<In, Out> { show(params: In): Out {/**  .   return ... */ } } //  - class PersonDialogOld extends BaseDialog<Person[], string> {/**   */} //   class PersonDialog extends BaseDialog<Person[], Person["id"]> {/**   */} 

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

 class SimpleBuilder { private constructor() {} static create(): SimpleBuilder { return new SimpleBuilder(); } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const builder = SimpleBuilder.create(); //     . const result = builder.firstName("F").lastName("L").middleName("M").build(); 

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

 type ExcludeMethod<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; type NarrowCallside<T> = { [P in keyof T]: T[P] extends (...args: any) => T ? ReturnType<T[P]> extends T ? (...args: Parameters<T[P]>) => NarrowCallside<ExcludeMethod<T, P>> : T[P] : T[P]; }; class SimpleBuilder { private constructor() {} static create(): NarrowCallside<SimpleBuilder> { return new SimpleBuilder(); } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const builder = SimpleBuilder.create(); const result = builder.firstName("F") // ^ -    .lastName("L") // ^ -   lastName, middleName  build .middleName("M") // ^ -   middleName  build .build(); // ^ -    build 

بحث : باستخدام نظام TypeScript ، يمكنك التحكم في تسلسل المكالمات من خلال تحديد ترتيب صارم. في المثال أدناه ، باستخدام نوع DirectCallside ، نوضح ذلك.

 type FilterKeys<T> = ({[P in keyof T]: T[P] extends (...args: any) => any ? ReturnType<T[P]> extends never ? never : P : never })[keyof T]; type FilterMethods<T> = Pick<T, FilterKeys<T>>; type BaseDirectCallside<T, Direct extends any[]> = FilterMethods<{ [Key in keyof T]: T[Key] extends ((...args: any) => T) ? ((..._: Direct) => any) extends ((_: infer First, ..._1: infer Next) => any) ? First extends Key ? (...args: Parameters<T[Key]>) => BaseDirectCallside<T, Next> : never : never : T[Key] }>; type DirectCallside<T, P extends Array<keyof T>> = BaseDirectCallside<T, P>; class StrongBuilder { private constructor() {} static create(): DirectCallside<StrongBuilder, ["firstName", "lastName", "middleName"]> { return new StrongBuilder() as any; } firstName(firstName: string): this { return this; } lastName(lastName: string): this { return this; } middleName(midleName: string): this { return this; } build(): string { return "what you needs"; } } const sBuilder = StrongBuilder.create(); const sResult = sBuilder.firstName("F") // ^ -   firstName  build .lastName("L") // ^ -   lastName  build .middleName("M") // ^ -   middleName  build .build(); // ^ -   build 

في المجموع



هذه هي كل ما عندي من نتائج مثيرة للاهتمام على TypeScript اليوم. شكرا لكم جميعا على اهتمامكم ونراكم قريبا.

Source: https://habr.com/ru/post/ar465267/


All Articles