اجعل جميع الخصائص داخل واجهة Typescript اختيارية
لدي واجهة في تطبيقي:
interface Asset {
id: string;
internal_id: string;
usage: number;
}
هذا جزء من واجهة المنشور:
interface Post {
asset: Asset;
}
لدي أيضًا واجهة تكون لمسودة منشور، حيث قد يكون كائن الأصول مُنشأ جزئيًا فقط
interface PostDraft {
asset: Asset;
}
أريد السماح لكائن PostDraft بأن يحتوي على كائن أصل جزئي بينما لا زلت أتحقق من أنواع الخصائص الموجودة (لذلك لا أريد فقط استبداله بـ any).
أريد أساسًا طريقة تمكنني من توليد ما يلي:
interface AssetDraft {
id?: string;
internal_id?: string;
usage?: number;
}
دون إعادة تعريف واجهة Asset بالكامل. هل هناك طريقة للقيام بذلك؟ إذا لم يكن، فما هي الطريقة الذكية لتنظيم أنواعبي في هذا الموقف؟
الإجابة رقم 1
هذا غير ممكن في TypeScript أقل من 2.1 دون إنشاء واجهة إضافية بخصائص اختيارية؛ ومع ذلك، هذا ممكن باستخدام الأنواع المربوطة في TypeScript 2.1 وما بعدها.
للقيام بذلك، استخدم النوع Partial<T> الذي يوفره TypeScript بشكل افتراضي.
interface PostDraft {
asset: Partial<Asset>;
}
الآن جميع الخصائص في asset اختيارية، مما سيسمح لك بفعل ما يلي:
const postDraft: PostDraft = {
asset: {
id: "some-id"
}
};
حول Partial<T>
Partial<T> يُعرف على أنه نوع مُعين يجعل كل خاصية في النوع المقدم اختيارية (باستخدام الرمز ؟).
type Partial<T> = {
[P in keyof T]?: T[P];
};
اقرأ المزيد عن الأنواع المرسومة هنا وفي الدليل.
جزئي عميق
إذا كنت تريد تنفيذًا جزئيًا يعمل بشكل متكرر على الكائنات، فيمكنك استخدام النوع التالي في TS 4.1+:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
الإجابة رقم 2
إذا أردت أن يكون لدي واجهة AssetDraft صريحة، سأستخدم مزيجًا من extends و Partial:
interface Asset {
id: string;
internal_id: string;
usage: number;
}
interface AssetDraft extends Partial<Asset> {}
الإجابة رقم 3
الخصائص في الواجهة إما اختيارية أو ليست كذلك، لا يمكنك استخدام نفس الواجهة مرة كاختيارية ومرة أخرى كإلزامية.
ما يمكنك فعله هو إنشاء واجهة تحتوي على خصائص اختيارية لـ AssetDraft ثم إنشاء فئة تحتوي على خصائص إلزامية لـ Asset:
interface AssetDraft {
id?: string;
internal_id?: string;
usage?: number;
}
class Asset {
static DEFAULT_ID = "id";
static DEFAULT_INTERNAL_ID = "internalid";
static DEFAULT_USAGE = 0;
id: string;
internal_id: string;
usage: number;
constructor(draft: AssetDraft) {
this.id = draft.id || Asset.DEFAULT_ID;
this.internal_id = draft.internal_id || Asset.DEFAULT_INTERNAL_ID;
this.usage = draft.usage || Asset.DEFAULT_USAGE;
}
}
القيم الافتراضية هنا هي أعضاء ثابتون، ولكن يمكنك الحصول عليها بطرق أخرى أو طرح خطأ في حال كانت مفقودة.
أجد هذه الطريقة مريحة جدًا عند العمل مع ملفات json المستلمة من الخادم (أو شيء مشابه)، حيث تمثل الواجهات بيانات json وتكون الفئات هي النماذج الفعلية التي يتم إنشاؤها باستخدام json كقيم أولية.
