توسيع كائن TypeScript العالمي في node.js


لدي تطبيق node.js يرفق بعض معلومات التكوين إلى الكائن global:

global.myConfig = {
    a: 1,
    b: 2
}

لا يحب مترجم TypeScript هذا لأن نوع Global ليس لديه كائن باسم myConfig:

TS2339: الخاصية 'myConfig' غير موجودة في النوع 'Global'.

لا أريد أن أفعل هذا:

global['myConfig'] = { ... }

كيف يمكنني إما توسيع نوع Global ليحتوي على myConfig أو مجرد إخبار TypeScript بالصمت والثقة بي؟ أفضل الخيار الأول.

لا أريد تغيير الإعلانات داخل node.d.ts. رأيت هذا المنشور على Stack Overflow وحاولت هذا:

declare module NodeJS  {
    interface Global {
        myConfig: any
    }
}

كطريقة لتوسيع واجهة Global الحالية، لكنها لا تبدو أن لها أي تأثير.

الإجابة رقم 1

اعتبارًا من node@16 تم إزالة واجهة NodeJS.Global لصالح globalThis.

يمكنك إعلان متغير عالمي جديد في ملف الوحدة كالتالي:

declare global {
  var NEW_GLOBAL: string;
}

وفي ملف غير وحدوي (بدون استيراد/تصدير على المستوى الأعلى) كما يلي:

declare var NEW_GLOBAL: string;

ملاحظة مهمة: يجب إعلان المتغير باستخدام var. المتغيرات let و const لا تظهر في globalThis.

الإجابة رقم 2

رأيت هذا المنشور في SO وجربت هذا:

من المحتمل أن يكون لديك شيء مثل vendor.d.ts:

// some import 
// AND/OR some export

declare module NodeJS  {
    interface Global {
        spotConfig: any
    }
}

يحتاج ملفك أن يكون نظيفًا من أي import أو exports على مستوى الجذر. هذا سيحوّل الملف إلى وحدة ويفصله عن مساحة إعلان النوع العالمي.

المزيد: https://basarat.gitbooks.io/typescript/content/docs/project/modules.html

الإجابة رقم 3

لتجنب أن يطالب Typescript بشيء مثل هذا:

TS2339: الخاصية 'myConfig' غير موجودة في النوع 'Global'.

أقترح تحديد أنواع مخصصة. أنا أفعل ذلك في ملف src/types/custom.d.ts في مشروعي:

declare global {
  namespace NodeJS {
    interface Global {
        myConfig: {
          a: number;
          b: number;
        }
    }
  }
}

ثم أضمن أن يتم أخذ هذه في الاعتبار من قبل TypeScript في ملف tsconfig.json:

{
  ...
  "files": [
    ...
    "src/types/custom.d.ts"
  ]
}

الآن أصبح بإمكانك استخدام الخاصية المخصصة الخاصة بك بأمان:

console.log(global.myConfig.a);

الإجابة رقم 4

وضع الملف التالي في الدليل الجذري لمشروعنا نجح.

global.d.ts

declare namespace NodeJS {
  export interface Global {
    myConfig: any
  }
}

نحن نستخدم "@types/node": "^7.0.18" و TypeScript الإصدار 2.3.4. ملف tsconfig.json الخاص بنا يبدو هكذا:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6"  
    },
    "exclude": [
        "node_modules"
    ]
}

الإجابة رقم 5

استخدم 'namespace' بدلاً من 'module' لإعلان TypeScript المخصص

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

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

ملاحظة: بينما سأل صاحب المنشور عن كائن حرفي، فإن العملية هي نفسها كما ترى أدناه. بدلاً من أن يكون نوع "Debug" دالة، ستقوم ببساطة بتعريف الواجهة كما تحتاج، ثم تغيير "debug:" إلى myConfig أو أي شيء ترغب به.

// typically I'll store the below in something like "typings.d.ts"
// this is because, at least typically, these overrides tend to
// be minimal in nature. You could break them up and Typescript
// will pick them up if you wish.

// Augmentations for the global scope can only be directly nested 
// in external modules or ambient module declarations.
export {}

declare global {
  // Definition or type for the function.
  type Debug = (label: string) => (message: any, ...args: any[]) => void

  // If defining an object you might do something like this
  // interface IConfig { a: number, b: number }

  // Extend the Global interface for the NodeJS namespace.
  namespace NodeJS {
    interface Global {
      // Reference our above type, 
      // this allows global.debug to be used anywhere in our code.
      debug: Debug
    }
  }
  
  // This allows us to simply call debug('some_label')('some debug message')
  // from anywhere in our code.
  const debug: Debug
}

كيف يمكن استخدام ما سبق

لإتمام المثال، كل ما فعلناه هنا هو تعريف متغير عام حتى نتمكن من تسجيل رسالة تصحيح بسيطة. إليك كيفية ربط الطريقة بسياقنا العام.

global.debug = (label: string) => (message: any, ...args: any[]) => console.log(message, ...args)

يمكننا أيضًا استدعاء طريقة التصحيح العالمية الخاصة بنا مباشرة:

debug('express')(`${req.method}: ${req.url}`)