Skip to content

ReactJSとTypeScript: 「refers to a value, but is being used as a type here」エラー(TS2749)の解決方法

問題

ReactJSとTypeScriptを使用してMaterial-UIコンポーネントを開発中、参照(ref)を作成しようとすると次のエラーが発生します:

'TextField' refers to a value, but is being used as a type here. TS2749

このエラーは、値として定義されているものを型として使用しようとした場合に発生します。

エラーの原因

TypeScriptでは、クラスは以下の2つの意味を持ちます:

  1. インスタンス型: クラスのインスタンスの型を表す
  2. コンストラクタ値: クラス自体のJavaScript値を表す

このエラーは、TypeScriptがクラスを値ではなく型として解釈すべき場所で、値として扱おうとしている場合に発生します。

解決方法

方法1: InstanceTypeを使用する(推奨)

最も一般的で安全な解決方法は、InstanceType<typeof ClassName>を使用することです:

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

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

解説

typeof TextFieldはクラスのコンストラクタ型を取得し、InstanceTypeはそのコンストラクタからインスタンス型を抽出します。これにより、値としてのTextFieldと型としてのTextFieldを明確に区別できます。

方法2: 型エイリアスの作成

頻繁に使用する場合は、型エイリアスを作成するとコードが簡潔になります:

typescript
import { TextField } from '@material-ui/core';

type TextFieldType = InstanceType<typeof TextField>;

export class MyTextField extends React.Component<MyProps, MyState> {
  private refTextField: React.RefObject<TextFieldType>;
  // ... 残りのコード
}

方法3: import typeの使用

TypeScript 3.8以降では、型のみをインポートする方法もあります:

typescript
import type { TextField } from '@material-ui/core';

// ただし、この方法ではTextFieldを値として使用できません
// (new TextField()や<TextField />のような使い方は不可能)

注意

import typeを使用すると、コンポーネントを値として使用できなくなるため、Reactコンポーネントでは通常推奨されません。

その他の一般的な原因と解決策

ファイル拡張子の問題

Reactコンポーネントを含むファイルは.tsx拡張子を使用する必要があります:

bash
my-component.ts
bash
my-component.tsx

ファイル拡張子を変更した後、開発サーバーを再起動してください。

import形式の問題

デフォルトインポートと名前付きインポートを混同している場合もあります:

typescript
import TextField from '@material-ui/core/TextField';
typescript
import { TextField } from '@material-ui/core';
// または
import TextField from '@material-ui/core/TextField'; // コンポーネントによって異なる

ヒント

使用しているライブラリの正しいインポート方法は、公式ドキュメントで確認してください。Material-UI v4とv5ではインポート方法が異なる場合があります。

インターフェースと型定義の混同

オブジェクトを型として定義する場合、インターフェースまたは型エイリアスを使用してください:

typescript
const User = {
  email: string
};
typescript
interface User {
  email?: string | undefined;
}
// または
type User = {
  email?: string | undefined;
};

まとめ

TS2749エラーは、TypeScriptが値と型を区別できない場合に発生します。ReactとTypeScriptで作業する際は、以下の点に注意してください:

  1. InstanceType<typeof Component>を使用してクラスコンポーネントの型を正しく取得する
  2. Reactコンポーネントは.tsxファイルに記述する
  3. ライブラリの正しいインポート形式を使用する
  4. 型定義にはインターフェースまたは型エイリアスを使用する

これらのプラクティスを守ることで、型安全性を保ちながらReactアプリケーションを効果的に開発できます。