ملفات الإعلانات وتعريفات الأنواع
ملفات الإعلانات وتعريفات الأنواع في TypeScript
ملفات الإعلانات (.d.ts) هي جزء أساسي من نظام TypeScript البيئي. توفر معلومات النوع لكود JavaScript، مما يتيح فحص الأنواع و IntelliSense للمكتبات التي لا تحتوي على تطبيقات TypeScript الخاصة بها. في هذا الدرس، سنستكشف كيفية العمل مع ملفات الإعلانات وإنشاء تعريفات الأنواع الخاصة بك.
فهم ملفات الإعلانات
يصف ملف الإعلان شكل وحدة أو مكتبة JavaScript موجودة دون احتواء التطبيق الفعلي. ملفات الإعلانات لها امتداد .d.ts وتحتوي فقط على معلومات النوع.
<// math-utils.d.ts
export function add(a: number, b: number): number;
export function subtract(a: number, b: number): number;
export function multiply(a: number, b: number): number;
export interface CalculatorOptions {
precision?: number;
roundingMode?: 'floor' | 'ceil' | 'round';
}
export class Calculator {
constructor(options?: CalculatorOptions);
calculate(expression: string): number;
clear(): void;
}
export const PI: number;
export const E: number;
>
مستودع DefinitelyTyped
DefinitelyTyped هو مستودع ضخم يحركه المجتمع لتعريفات الأنواع لآلاف مكتبات JavaScript. يتم نشر هذه التعريفات على npm تحت نطاق @types.
<# تثبيت تعريفات الأنواع لمكتبة
npm install --save-dev @types/lodash
npm install --save-dev @types/express
npm install --save-dev @types/node
npm install --save-dev @types/react
# التحقق من الأنواع المتاحة
npm search @types/library-name
# يتم اكتشاف الأنواع تلقائيًا بواسطة TypeScript
# في node_modules/@types/
>
npm search @types/package-name أو قم بزيارة TypeSearch.
كتابة الإعلانات المحيطة
تصف الإعلانات المحيطة الأنواع الموجودة في مكان آخر، عادةً في كود JavaScript أو المكتبات الخارجية. تستخدم الكلمة المفتاحية declare.
<// global.d.ts - الإعلانات المحيطة للمتغيرات العامة
// الإعلان عن متغير عام
declare const API_URL: string;
declare const VERSION: string;
// الإعلان عن دالة عامة
declare function gtag(
command: 'config' | 'event',
targetId: string,
config?: object
): void;
// الإعلان عن فئة عامة
declare class jQuery {
constructor(selector: string);
addClass(className: string): this;
removeClass(className: string): this;
on(event: string, handler: Function): this;
}
// الإعلان عن مساحة اسم عامة
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: 'development' | 'production' | 'test';
DATABASE_URL: string;
API_KEY: string;
}
}
// الآن يمكن استخدام هذه دون أخطاء
console.log(API_URL);
gtag('event', 'page_view');
const $el = new jQuery('#app');
const env = process.env.NODE_ENV;
>
إعلانات الوحدات
عند العمل مع وحدات JavaScript التي لا تحتوي على تعريفات أنواع، يمكنك الإعلان عن أنواعها باستخدام إعلانات الوحدات.
<// declarations.d.ts
// الإعلان عن أنواع لوحدة طرف ثالث
declare module 'legacy-library' {
export function processData(data: string): any;
export class DataProcessor {
constructor(options?: object);
process(input: string): string;
}
}
// الإعلان عن وحدة حرف بدل لاستيرادات الملفات
declare module '*.css' {
const content: { [className: string]: string };
export default content;
}
declare module '*.png' {
const value: string;
export default value;
}
declare module '*.svg' {
import React = require('react');
export const ReactComponent: React.FC<React.SVGProps<SVGSVGElement>>;
const src: string;
export default src;
}
// الإعلان عن وحدات JSON
declare module '*.json' {
const value: any;
export default value;
}
// الآن يمكنك استيراد هذه الملفات
import styles from './styles.css';
import logo from './logo.png';
import config from './config.json';
>
any يعطل فحص الأنواع. قدم أنواعًا محددة عندما يكون ذلك ممكنًا.
إنشاء ملفات الإعلانات لمكتبتك
عند نشر مكتبة TypeScript، يجب عليك إنشاء ملفات الإعلانات حتى يتمكن المستهلكون من الاستفادة من فحص الأنواع.
<{
"compilerOptions": {
"declaration": true, // إنشاء ملفات .d.ts
"declarationMap": true, // إنشاء خرائط المصدر لـ .d.ts
"emitDeclarationOnly": false, // أيضًا إصدار ملفات JS
"outDir": "./dist", // دليل الإخراج
"declarationDir": "./dist/types" // اختياري: دليل منفصل لـ .d.ts
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.test.ts"]
}
>
<// src/index.ts
export interface UserConfig {
apiUrl: string;
timeout?: number;
retries?: number;
}
export class ApiClient {
constructor(config: UserConfig) {
// التنفيذ
}
async get<T>(endpoint: string): Promise<T> {
// التنفيذ
return {} as T;
}
async post<T>(endpoint: string, data: any): Promise<T> {
// التنفيذ
return {} as T;
}
}
export function createClient(config: UserConfig): ApiClient {
return new ApiClient(config);
}
// بعد التجميع، ينشئ TypeScript:
// dist/index.js (التنفيذ)
// dist/index.d.ts (إعلانات الأنواع)
>
<// dist/index.d.ts (مُنشأ تلقائيًا)
export interface UserConfig {
apiUrl: string;
timeout?: number;
retries?: number;
}
export declare class ApiClient {
constructor(config: UserConfig);
get<T>(endpoint: string): Promise<T>;
post<T>(endpoint: string, data: any): Promise<T>;
}
export declare function createClient(config: UserConfig): ApiClient;
>
تكوين نوع Package.json
قم بتكوين package.json للإشارة إلى تعريفات الأنواع الصحيحة لمستهلكي حزمة npm.
<{
"name": "my-library",
"version": "1.0.0",
"main": "./dist/index.js", // نقطة الدخول لـ CommonJS
"module": "./dist/index.esm.js", // نقطة الدخول لوحدات ES
"types": "./dist/index.d.ts", // إدخال تعريفات الأنواع
"typings": "./dist/index.d.ts", // بديل لـ "types"
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.js",
"types": "./dist/index.d.ts"
},
"./utils": {
"import": "./dist/utils.esm.js",
"require": "./dist/utils.js",
"types": "./dist/utils.d.ts"
}
},
"files": [
"dist"
]
}
>
"exports" لتكوين نقاط دخول الحزمة الحديثة مع دعم الأنواع المناسب.
توجيهات الشرطة الثلاثية
توجيهات الشرطة الثلاثية هي تعليقات من سطر واحد توفر تعليمات المترجم وهي صالحة فقط في أعلى الملف.
</// <reference path="./custom-types.d.ts" />
/// <reference types="node" />
/// <reference lib="es2020" />
// الإشارة إلى ملف إعلان آخر
/// <reference path="./globals.d.ts" />
// الإشارة إلى حزمة @types
/// <reference types="jquery" />
// الإشارة إلى مكتبة TypeScript
/// <reference lib="dom" />
/// <reference lib="es2021" />
// تخبر هذه التوجيهات TypeScript بتضمين
// تعريفات أنواع محددة أثناء التجميع
>
tsconfig.json واستيرادات ES6.
توسيع الأنواع الموجودة
يمكنك توسيع الأنواع الموجودة من المكتبات باستخدام دمج الإعلانات. هذا مفيد لإضافة خصائص أو طرق مخصصة.
<// custom.d.ts
// توسيع نوع Express Request
import { Express } from 'express';
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
role: 'admin' | 'user';
};
requestId: string;
}
}
}
// توسيع واجهة Window
interface Window {
gtag: (command: string, ...args: any[]) => void;
dataLayer: any[];
myCustomProperty: string;
}
// توسيع نموذج Array الأولي
interface Array<T> {
first(): T | undefined;
last(): T | undefined;
}
// الآن يمكنك استخدام هذه الأنواع الموسعة
// في كودك دون أخطاء
// وسيط Express
app.use((req, res, next) => {
req.requestId = generateId(); // لا يوجد خطأ
if (req.user) {
console.log(req.user.role); // لا يوجد خطأ
}
next();
});
// كود المتصفح
window.gtag('event', 'page_view'); // لا يوجد خطأ
console.log(window.myCustomProperty); // لا يوجد خطأ
// امتدادات المصفوفة
const arr = [1, 2, 3];
arr.first(); // لا يوجد خطأ
arr.last(); // لا يوجد خطأ
>
إعلانات مساحة الاسم
مساحات الأسماء (تسمى سابقًا "الوحدات الداخلية") تنظم الكود وتمنع تلوث مساحة الاسم العامة في ملفات الإعلانات.
<// my-library.d.ts
declare namespace MyLibrary {
// الواجهات
interface Config {
apiKey: string;
debug?: boolean;
}
interface User {
id: string;
name: string;
}
// الفئات
class Client {
constructor(config: Config);
getUser(id: string): Promise<User>;
}
// الدوال
function init(config: Config): Client;
function version(): string;
// مساحة اسم متداخلة
namespace Utils {
function formatDate(date: Date): string;
function parseJSON<T>(json: string): T;
}
// الثوابت
const VERSION: string;
}
// الاستخدام
const client = MyLibrary.init({ apiKey: 'xyz' });
const user = await client.getUser('123');
const formatted = MyLibrary.Utils.formatDate(new Date());
console.log(MyLibrary.VERSION);
>
مثال عملي: إنشاء إعلان مكون إضافي
<// types/jquery-plugin.d.ts
// توسيع jQuery مع مكون إضافي مخصص
interface JQuery {
/**
* مكون إضافي لتلميحات الأدوات المخصص
* @param options - خيارات التكوين
*/
myTooltip(options?: MyTooltip.Options): JQuery;
/**
* عرض تلميح الأداة
*/
myTooltip(action: 'show'): JQuery;
/**
* إخفاء تلميح الأداة
*/
myTooltip(action: 'hide'): JQuery;
/**
* تدمير تلميح الأداة
*/
myTooltip(action: 'destroy'): JQuery;
}
// الإعلان عن مساحة اسم المكون الإضافي
declare namespace MyTooltip {
interface Options {
content?: string;
placement?: 'top' | 'bottom' | 'left' | 'right';
trigger?: 'hover' | 'click' | 'manual';
delay?: number;
animation?: boolean;
template?: string;
onShow?: () => void;
onHide?: () => void;
}
interface API {
show(): void;
hide(): void;
toggle(): void;
destroy(): void;
update(content: string): void;
}
const defaults: Options;
const version: string;
}
// أمثلة الاستخدام
$('#element').myTooltip({
content: 'مرحبًا بالعالم',
placement: 'top',
trigger: 'hover'
});
$('#element').myTooltip('show');
$('#element').myTooltip('hide');
console.log(MyTooltip.version);
>
أنماط الإعلانات الشائعة
<// 1. تحميل الدالة الزائد
declare function createElement(tag: 'div'): HTMLDivElement;
declare function createElement(tag: 'span'): HTMLSpanElement;
declare function createElement(tag: string): HTMLElement;
// 2. واجهات قابلة للاستدعاء
interface ClickHandler {
(event: MouseEvent): void;
namespace: string;
version: string;
}
declare const onClick: ClickHandler;
// 3. توقيعات المنشئ
interface UserConstructor {
new (name: string): User;
new (name: string, email: string): User;
readonly prototype: User;
}
declare const User: UserConstructor;
// 4. أنواع هجينة (قابلة للاستدعاء والإنشاء)
interface JQueryStatic {
(selector: string): JQuery;
ajax(url: string, settings?: any): any;
version: string;
}
declare const $: JQueryStatic;
// 5. قيود عامة في الإعلانات
declare function map<T, U>(
array: T[],
fn: (item: T, index: number) => U
): U[];
// 6. أنواع شرطية في الإعلانات
declare function process<T>(
value: T
): T extends string ? string : number;
>
- كائن
analyticsعام مع طرقtrackوidentify - تعريفات الأنواع لخصائص الأحداث وسمات المستخدم
- أحمال طرق زائدة لمجموعات معاملات مختلفة
- مساحة اسم لخيارات التكوين
اختبار إعلاناتك
<// test-types.ts - اختبر إعلانات أنواعك
import { ApiClient, UserConfig } from './index';
// تأكيدات النوع للتحقق من الأنواع
const config: UserConfig = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
const client = new ApiClient(config);
// يجب أن يُجمع هذا بدون أخطاء
(async () => {
const user = await client.get<{ id: string }>('/user');
const id: string = user.id; // يجب أن يكون string
await client.post('/user', { name: 'جون' });
})();
// استخدم dtslint أو tsd للاختبار الآلي للإعلانات
// npm install --save-dev dtslint
// npm install --save-dev tsd
>
tsd أو dtslint لكتابة اختبارات لإعلانات أنواعك، لضمان عملها كما هو متوقع.