ReactJS و Typescript: تشير إلى قيمة، لكنها تُستخدم هنا كنمط (TS2749)


أنا أقوم ببرمجة فئة ReactJS باستخدام Typescript و Material-ui، في ملف .tsx. في أحد المكونات المخصصة الخاصة بي، أريد إنشاء مرجع لأحد المكونات التي أستخدمها في مكوني المخصص.

export class MyTextField extends React.Component<MyProps, MyState> {
  private refTextField: React.RefObject<TextField>;
  constructor(props: MyProps) {
    super(props);
    this.refTextField = React.createRef();
  }

  render(): JSX.Element {
    const { id, label, value: defaultValue } = this.props;
    const { value } = this.state;
    const element = (
      <TextField ref={this.refTextField} id={id} label={label} defaultValue={defaultValue} value={value} />
    );

    return element;
  }
}

أثناء التجميع، أحصل على خطأ في إعلان مرجعتي:

يشير 'TextField' إلى قيمة، ولكنه يُستخدم هنا كنوع. TS2749

حاولت وضع "typeof TextField" في إعلاني، لكن لدي رسالة أخرى عند تعيين قيمة الخاصية ref في العرض الخاص بي :

النوع 'RefObject<(props: TextFieldProps) => Element>' غير قابل للتخصيص للكتابة '((instance: HTMLDivElement | null) => void) | RefObject | فارغة | غير محدد". النوع 'RefObject<(props: TextFieldProps) => Element>' غير قابل للتخصيص لكتابة 'RefObject'. النوع '(props: TextFieldProps) => Element' يفتقد الخصائص التالية من النوع 'HTMLDivElement': align وaddEventListener وremoveEventListener وaccessKey و238 خاصية أخرى. TS2322

أي أفكار؟ شكراً جزيلاً

الإجابة رقم 1

تأكد من أنك تعمل على ملف .tsx وليس على ملف .ts

الإجابة رقم 2

تغيير ملف .ts إلى .tsx

في حالتي، هذا يعمل

الإجابة رقم 3

لقد واجهت هذه المشكلة عدة مرات في رمزي من قبل لكنني اكتشفت سبب حدوث ذلك فقط اليوم.

ختصار: في حالتك، عليك فقط كتابة InstanceType<typeof TextField> بدلاً من TextField.

عندما تنشئ فئة في TypeScript، يشير اسم تلك الفئة إلى كل من نوع النسخة وقيمة الفئة في جافا سكريبت. إذا قمت بالإشارة إلى تلك الفئة كنوع، يكتشف TypeScript ذلك تلقائياً كنمط النسخة لتلك الفئة. وإذا قمت بالإشارة إلى تلك الفئة أثناء وقت التشغيل، فإنه يستخدمها ببساطة بالمعنى الموجود في جافا سكريبت. وكل شيء جيد حتى الآن.

class MyClass {}
let abc: MyClass; // ts recognizes as instance type
abc = new MyClass(); // completely fine, used here as the javascript value

ومع ذلك، المشكلة الحقيقية هي عندما تقوم بتصدير الصنف من وحدة ديناميكياً. عندما تقوم بتصدير الصنف بطرق معينة، يمكن لـ TypeScript تصدير القيمة الجافاسكريبتية فقط للصنف ولا تصدّر النوع. لذا إذا قمت باستيراده في وحدة أخرى وحاولت الإشارة إليه كنمط، فستحصل على TS2749.

let intervariable = class MyClass{}
export const MyClass = intervariable; // TypeScript does not export type here.
import {MyClass} from './myclass';

let abc: MyClass; // TypeScript error TS2749

عندما يحدث هذا، خاصة إذا كان خارج سيطرتك، كان حلي للحصول على نوع المثال ببساطة هو استخدام InstanceType و typeof:

import {MyClass} from './myclass';

let abc: InstanceType<typeof MyClass>; // no error
// the rest...

عامل typeof يحصل لك على نوع منشئ الفئة للقيمة الفئوية، و Generic InstanceType يحصل لك على نوع النسخة التي تريدها.

الإجابة رقم 4

أولاً غيّر نوع الملف .tsx

ثم اضغط على F1 واكتب "إعادة تحميل النافذة"

:)

الإجابة رقم 5

كان يجب أن يكون هذا تعليقًا، لكن ليس لدي سمعة كافية. أردت أن أشارك أن طريقة نظيفة لاستخدام الحل الموصى به من الإجابة المقبولة هي إعلان اسم مستعار لنوع بنفس الاسم (لا يسبب تعارضًا). بعد ذلك يمكنك استخدام نوعك بنفس الطريقة التي كنت تستخدم بها عادة نوع المثيل.

إضافة إلى المثال:

import {MyClass} from './myclass';

type MyClass = InstanceType<typeof MyClass>;

let abc: MyClass; // still no error!

تعديل: يبدو أن استيراد النوع يعمل أيضًا ويبدو أنظف:

import type {MyClass} from './myclass';

التحذير مع نهج import type هو أنه لا يمكنك استخدامه كقيمة (مثل إنشاء new MyClass(...)).