Skip to content

TypeScript مع واجهات الخيارات ​

لقراءة هذه الصفحة يجب عليك أولا الاطلاع على استخدام Vue مع TypeScript.

ملاحظة

على الرغم من أن Vue تدعم استخدام TypeScript مع واجهات الخيارات ، إلا أنه يفضل استخدام Vue مع TypeScript عبر الواجهة التركيبية لأنه تقدم استنباط أكثر بساطة وفعالية وأكثر صلابة للأنواع.

تحديد النوع للخاصيات ​

استنباط النوع للخاصيات في واجهة الخيارات يتطلب تغليف المكون بالدالة ()defineComponent. بالاعتماد على ذلك، يمكن لـ Vue استنباط أنواع الخاصيات بناءً على خيار props ، وذلك بالاعتماد على خيارات إضافية مثل required: true و default:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  //  تمكين استنباط النوع
  props: {
    name: String,
    id: [Number, String],
    msg: { type: String, required: true },
    metadata: null
  },
  mounted() {
    this.name // type: string | undefined
    this.id // type: number | string | undefined
    this.msg // type: string
    this.metadata // type: any
  }
})

ومع ذلك ، لا تدعم خيارات props الوقت التشغيلي استخدام دوال المنشئات كنوع خاصية - لا توجد طريقة لتحديد أنواع معقدة مثل الكائنات مع الخاصيات المتداخلة أو توقيعات استدعاء الدوال.

لتوصيف أنواع خاصيات معقدة ، يمكننا استخدام نوع PropType:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  author: string
  year: number
}

export default defineComponent({
  props: {
    book: {
      // توفير نوع أكثر تحديدًا لـ `Object`
      type: Object as PropType<Book>,
      required: true
    },
    // يمكنك أيضًا توصيف الدوال
    callback: Function as PropType<(id: number) => void>
  },
  mounted() {
    this.book.title // string
    this.book.year // number

    //  خطأ TS: لا يمكن تعيين نوع الوسيط 
    // 'string' إلى نوع الوسيط 'number'
    this.callback?.('123')
  }
})

تنبيهات ​

إذا كان إصدار TypeScript الخاص بك أقل من 4.7 ، فيجب عليك الانتباه عند استخدام قيم الدوال لخيارات خاصية validator و default - تأكد من استخدام الدوال السهمية:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  year?: number
}

export default defineComponent({
  props: {
    bookA: {
      type: Object as PropType<Book>,
      // تأكد من استخدام الدوال السهمية إذا كان إصدار TypeScript الخاص بك أقل من 4.7
      default: () => ({
        title: 'Arrow Function Expression'
      }),
      validator: (book: Book) => !!book.title
    }
  }
})

هذا يمنع TypeScript من تحديد نوع this داخل هذه الدوال ، وللأسف ، يمكن أن يؤدي ذلك إلى فشل استنباط النوع. كانت قيود التصميم سابقة, والآن قد حسنت في الإصدار TypeScript 4.7.

تحديد النوع للإرسالات ​

يمكننا تعريف نوع بيانات الحمولة المتوقع للحدث المرسل باستخدام صيغة الكائن لخيار emits. كما يمكن أن ترسل جميع الأحداث غير المعرفة بالإعلان عن خطأ في النوع عند الاستدعاء:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  emits: {
    addBook(payload: { bookName: string }) {
      // تنفيذ التحقق من الصحة في الوقت التشغيلي
      return payload.bookName.length > 0
    }
  },
  methods: {
    onSubmit() {
      this.$emit('addBook', {
        bookName: 123 // خطأ في النوع
      })

      this.$emit('non-declared-event')// خطأ في النوع
    }
  }
})

تحديد النوع للخاصيات المحسوبة ​

تحدد الخاصية المحسوبة نوعها بناءً على قيمة الإرجاع:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Hello!'
    }
  },
  computed: {
    greeting() {
      return this.message + '!'
    }
  },
  mounted() {
    this.greeting // type: string
  }
})

في بعض الحالات ، قد ترغب في توصيف نوع خاصية محسوبة بشكل صريح لضمان صحة تنفيذها:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Hello!'
    }
  },
  computed: {
    // توصيف نوع الإرجاع بشكل صريح
    greeting(): string {
      return this.message + '!'
    },

    // توصيف خاصية محسوبة قابلة للكتابة
    greetingUppercased: {
      get(): string {
        return this.greeting.toUpperCase()
      },
      set(newValue: string) {
        this.message = newValue.toUpperCase()
      }
    }
  }
})

قد تكون التوصيفات الصريحة مطلوبة أيضًا في بعض الحالات الحدودية التي يفشل TypeScript في تحديد نوع خاصية محسوبة بسبب حلقات استنباط دائرية.

تحديد النوع لمعالجات الأحداث ​

عند التعامل مع أحداث DOM الأصلية ، قد يكون من المفيد تحديد النوع الصحيح للوسيط الذي نمرره إليه. دعونا نلقي نظرة على هذا المثال:

vue
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  methods: {
    handleChange(event) {
      // الوسيط `event` يحتوي على نوع `any` بشكل ضمني
      console.log(event.target.value)
    }
  }
})
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

بدون توصيف النوع ، سيكون الوسيط event ضمنيًا بنوع any. سيؤدي هذا أيضًا إلى خطأ TS إذا كان "strict": true أو "noImplicitAny": true يتم استخدامهما في tsconfig.json. لذلك ، يُوصى بتوصيف الوسيط الوارد من معالجات الأحداث بشكل صريح. بالإضافة إلى ذلك ، قد تحتاج إلى تحويل صريح للخاصيات على event:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  methods: {
    handleChange(event: Event) {
      console.log((event.target as HTMLInputElement).value)
    }
  }
})

توسيع الخاصيات العامة ​

بعض الملحقات تثبت خاصيات متاحة على المستوى العام لجميع نسخ المكونات عبر app.config.globalProperties. على سبيل المثال ، قد نثبت this.$http لجلب البيانات أو this.$translate للترجمة التوطينية. لجعل هذا يعمل بشكل جيد مع TypeScript ، يوفر Vue واجهة ComponentCustomProperties المصممة للتوسيع عبر توسيع وحدة TypeScript:

ts
import axios from 'axios'

declare module 'vue' {
  interface ComponentCustomProperties {
    $http: typeof axios
    $translate: (key: string) => string
  }
}

اطلع أيضا على

موضع توسيع النوع ​

يمكننا وضع توسيع النوع في ملف ts. ، أو في ملف d.ts.* على مستوى المشروع. بأي طريقة من الطرق ، تأكد من تضمينه في tsconfig.json. لمؤلفي الملحقات / المكتبات ، يجب تحديد هذا الملف في خاصية types في package.json.

للاستفادة من توسيع الوحدة ، ستحتاج إلى التأكد من وضع التوسيع في وحدة TypeScript. أي أن الملف يحتاج إلى أن يحتوي على import أو export على المستوى الأعلى واحد على الأقل ، حتى لو كان مجرد export {}. إذا وُضع التوسيع خارج الوحدة، فسيقوم بالكتابة فوق أصل الأنواع بدلاً من توسيعها!

ts
// لا يعمل ، يكتب فوق أصل الأنواع.
declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}
ts
// يعمل بشكل صحيح
export {}

declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}

توسيع الخيارات المخصصة ​

بعض الملحقات ، على سبيل المثال vue-router ، توفر دعم خيارات المكونات المخصصة مثل beforeRouteEnter:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  beforeRouteEnter(to, from, next) {
    // ...
  }
})

بدون توسيع نوع صحيح ، ستكون وسائط هذا الخطاف ذات نوع any بشكل ضمني. يمكننا توسيع واجهة ComponentCustomOptions لدعم هذه الخيارات المخصصة:

ts
import { Route } from 'vue-router'

declare module 'vue' {
  interface ComponentCustomOptions {
    beforeRouteEnter?(to: Route, from: Route, next: () => void): void
  }
}

الآن سيكون خيار beforeRouteEnter محدد النوع بشكل صحيح. لاحظ أن هذا مجرد مثال - يجب أن تقوم المكتبات محددة الأنواع بشكل جيد مثل vue-router بتنفيذ هذه التوسيعات بشكل تلقائي في تعريفات أنواعها الخاصة.

موضع هذا التوسيع يعتمد على نفس القيود مثل توسيع الخاصيات العامة.

اطلع أيضا على

TypeScript مع واجهات الخيارات has loaded