كيفية توسيع نموذج السلسلة واستخدامه لاحقًا في تايب سكريبت؟
أنا أقوم بتمديد سلسلة بروتوتايب String بسلسلة جديدة من الطرق، ولكن عندما أحاول استخدامها يظهر لي خطأ: الخاصية 'padZero' غير موجودة على النوع 'string'. هل يمكن لأي شخص حل هذا لي؟
الكود أدناه. يمكنك أيضًا رؤية نفس الخطأ في Typescript Playground.
interface NumberConstructor {
padZero(length: number);
}
interface StringConstructor {
padZero(length: number): string;
}
String.padZero = (length: number) => {
var s = this;
while (s.length < length) {
s = '0' + s;
}
return s;
};
Number.padZero = function (length) {
return String(this).padZero(length);
}
الإجابة رقم 1
ينطبق هذا الجواب على TypeScript 1.8 وما فوق. هناك الكثير من الإجابات الأخرى على هذا النوع من الأسئلة، لكنها كلها تبدو أنها تغطي الإصدارات القديمة.
هناك جزآن لتمديد نموذج أولي في TypeScript.
الجزء 1 - الإعلان
إعلان العضو الجديد بحيث يمكنه اجتياز فحص النوع. تحتاج إلى إعلان واجهة بنفس اسم الباني/الفئة التي تريد تعديلها ووضعها تحت مساحة الأسماء/الوحدة المعلنة الصحيحة. يُطلق على هذا اسم توسيع النطاق.
يمكن تمديد الوحدات بهذه الطريقة فقط في ملفات الإعلانات الخاصة .d.ts.
//in a .d.ts file:
declare global {
interface String {
padZero(length : number) : string;
}
}
الأنواع في الوحدات الخارجية لها أسماء تتضمن علامات الاقتباس، مثل "bluebird".
اسم الوحدة للأنواع العالمية مثل Array<T> و String هو global، بدون أي علامات اقتباس. ومع ذلك، في العديد من إصدارات TypeScript يمكنك الاستغناء عن إعلان الوحدة تمامًا وإعلان واجهة مباشرة، للحصول على شيء مثل:
declare interface String {
padZero(length : number) : string;
}
هذا هو الحال في بعض الإصدارات قبل 1.8، وأيضًا بعض الإصدارات بعد 2.0، مثل أحدث إصدار، 2.5.
لاحظ أنه لا يمكنك وضع أي شيء آخر سوى تعليمات declare في ملف .d.ts، وإلا فلن يعمل.
يتم إضافة هذه الإعلانات إلى النطاق العام لحزمتك، بحيث ستنطبق في جميع ملفات TypeScript حتى إذا لم تقم أبدًا بـ import أو ///<reference الملف مباشرة. ومع ذلك، لا يزال يتعين عليك استيراد التنفيذ الذي تكتبه في الجزء الثاني، وإذا نسيت القيام بذلك، ستواجه أخطاءً أثناء وقت التشغيل.
* من الناحية الفنية، يمكنك تجاوز هذا القيد ووضع التعريفات في ملفات .ts العادية، ولكن هذا يؤدي إلى بعض السلوكيات الغريبة من قبل المجمّع، ولا يُنصح به.
الجزء 2 - التنفيذ
الجزء 2 هو في الواقع تنفيذ العضو وإضافته إلى الكائن الذي يجب أن يكون موجودًا عليه كما تفعل في جافا سكريبت.
String.prototype.padZero = function (this : string, length: number) {
var s = this;
while (s.length < length) {
s = '0' + s;
}
return s;
};
لاحظ بعض الأشياء:
String.prototypeبدلاً من مجردString، وهو منشئString، بدلاً من النموذج الأولي الخاص به.- أنا أستخدم
دالةصريحة بدلاً من دالة السهم، لأنالدالةستستقبل بشكل صحيح معاملthisمن المكان الذي تم استدعاؤها فيه. أما دالة السهم فستستخدم دائماً نفسthisالموجود في المكان الذي تم إعلانها فيه. المرة الوحيدة التي لا نريد أن يحدث ذلك هي عند توسيع نموذج أولي (prototype). - الـ
thisالصريح، حتى يعرف المترجم نوع الـthisالذي نتوقعه. هذا الجزء متاح فقط في TS 2.0+. قم بإزالة تعليق الـthisإذا كنت تقوم بالترجمة لـ 1.8-. بدون التعليق، قد يتم تحديد نوع الـthisضمنيًا على أنهany.
استورد جافاسكريبت
في TypeScript، تضيف صيغة declare أعضاءً إلى النطاق المحيط، مما يعني أنهم يظهرون في جميع الملفات. لضمان أن يكون تنفيذك من الجزء 2 متصلاً، قم بـ import الملف في بداية تطبيقك.
تستورد الملف كما يلي:
import '/path/to/implementation/file';
دون استيراد أي شيء. يمكنك أيضًا استيراد شيء من الملف، لكن لا تحتاج إلى استيراد الدالة التي قمت بتعريفها على النموذج الأولي.
الإجابة رقم 2
إذا كنت تريد تعزيز class، وليس instance، عزز الباني:
declare global {
interface StringConstructor {
padZero(s: string, length: number): string;
}
}
String.padZero = (s: string, length: number) => {
while (s.length < length) {
s = '0' + s;
}
return s;
};
console.log(String.padZero('hi', 5))
export {}
*التصدير الفارغ في الأسفل مطلوب إذا قمت بـ declare global في .ts ولم تصدر أي شيء. هذا يجبر الملف على أن يكون وحدة.*
إذا أردت الدالة على instance (المعروفة أيضًا باسم prototype)،
declare global {
interface String {
padZero(length: number): string;
}
}
String.prototype.padZero = function (length: number) {
let d = String(this)
while (d.length < length) {
d = '0' + d;
}
return d;
};
console.log('hi'.padZero(5))
export {}
الإجابة رقم 3
إليك مثالاً عملياً، مُعدّل سلسلة نصية بسيط بأسلوب Camel Case.
في index.d.ts الخاص بمشروعي
interface String {
toCamelCase(): string;
}
في الجذر الخاص بي .ts في مكان يمكن الوصول إليه
String.prototype.toCamelCase = function(): string {
return this.replace(/(?:^\w|[A-Z]|-|\b\w)/g,
(ltr, idx) => idx === 0
? ltr.toLowerCase()
: ltr.toUpperCase()
).replace(/\s+|-/g, '');
};
هذا كل ما كنت بحاجة للقيام به لتشغيله في تايب سكريبت ^2.0.10.
أستخدمه مثل str.toCamelCase()
تحديث
أدركت أن لدي هذه الحاجة أيضًا وهذا ما كان لدي
interface String {
leadingChars(chars: string|number, length: number): string;
}
String.prototype.leadingChars = function (chars: string|number, length: number): string {
return (chars.toString().repeat(length) + this).substr(-length);
};
لذلك console.log('1214'.leadingChars('0', 10)); يعطي لي 0000001214
الإجابة رقم 4
بالنسبة لي، الأمور التالية نجحت في مشروع Angular 6 باستخدام TypeScript 2.8.4.
في ملف typings.d.ts أضف:
interface Number {
padZero(length: number);
}
interface String {
padZero(length: number);
}
ملاحظة: لا داعي لـ 'إعلان عالمي'.
في ملف جديد يسمى string.extensions.ts أضف ما يلي:
interface Number {
padZero(length: number);
}
interface String {
padZero(length: number);
}
String.prototype.padZero = function (length: number) {
var s: string = String(this);
while (s.length < length) {
s = '0' + s;
}
return s;
}
Number.prototype.padZero = function (length: number) {
return String(this).padZero(length)
}
لاستخدامه، استورده أولاً:
import '../../string.extensions';
من الواضح أن جملة الاستيراد الخاصة بك يجب أن تشير إلى المسار الصحيح.
داخل منشئ الفصل الخاص بك أو أي طريقة:
var num: number = 7;
var str: string = "7";
console.log("padded number: ", num.padZero(5));
console.log("padding string: ", str.padZero(5));
الإجابة رقم 5
2022 تايبسكريبت 4.9
الحل بسيط جدًا لكن الوصول إلى هذا الحل لم يكن كذلك. تجاهل الإجابة التي حصلت على أعلى الأصوات؛ معظمها غير ضروري ولا يعمل بعد الآن.
index.d.ts
غير مطلوب!
strings.ts
interface String {
toProperCase(): string;
bool(): boolean;
}
String.prototype.toProperCase = function (): string {
return this.toLowerCase().replace(/\b\w/g, (c: string) => c.toUpperCase())
}
String.prototype.bool = function (): boolean {
return this.toLowerCase() == 'true';
};
باستخدامه
import './lib/strings'
var a = 'hello'
var b = a.toProperCase()
المخرجات :
مرحبا
