問題
React/TypeScriptでreduxやmobxを使用した場合、コンポーネントのプロパティに関する型エラーが出ることがあります。例えば、以下のようにmobx
でProvider
を用いて暗にmessageStore
を注入したとします。
import { observable } from "mobx";
import { inject, observer, Provider } from "mobx-react";
import * as React from "react";
import * as ReactDOM from "react-dom";
class MessageStore {
@observable public message: string = "";
}
@inject("messageStore")
@observer
class App extends React.Component<{ messageStore: MessageStore }> {
public render() {
return <div>{this.props.messageStore.message}</div>;
}
}
const messageStore = new MessageStore();
ReactDOM.render(
<Provider messageStore={messageStore}><App /></Provider>,
document.getElementById("root"));
上記のコードをコンパイルしようとすると以下のエラーが出ます。
Type '{}' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<App> & Readonly<{ children?: ReactNode; }> & Reado...'.
Type '{}' is not assignable to type 'Readonly<{ messageStore: MessageStore; }>'.
Property 'messageStore' is missing in type '{}'.
これは<App />
でコンポーネントを使用するときにmessageStore
のプロパティが渡されていないために起こります。しかし、実際にはProvider
によって暗に渡されているためこのコードは正しく動作します。問題はmobxのinject
デコレータが元のクラス型を上手く加工してくれないことです。因みに、reduxのconnect
関数の使用時にも似たような問題が起こります。
解決策
React.Component
の型引数にany
を渡したくなりますが、もう少しいい方法があります。messageStore
をオプショナルにします。この場合でも、いちいちmessageStore
がプロパティに存在するか確認しなくてもthis.props.messageStore
で使用できます。
class App extends React.Component<{ messageStore?: MessageStore }> {
...
}
Partial
型を使うとstoreが複数ある場合に便利です。
interface IStore {
messageStore: MessageStore;
anotherStore: AnotherStore;
}
class App extends React.Component<Partial<IStore>> {
...
}