React, TypeScriptでenvを読み込みたいが型がundefinedになる
create-react-app -- template typescript
で作ったプロジェクトに環境変数を読み込んで使いたい時の対処法です。
下記のように環境変数を読み込ませたいのですが、この場合WEB_URLの部分はstring | undefined
と推論されます。
const hoge = process.env.WEB_URL; // const hoge: string | undefined
process.envからコードジャンプを使って型を追っていくと、node_modules/@types/node/global.d.ts
の中に、このような記述があることがわかります。
interface Dict<T> { [key: string]: T | undefined; }
値が T または undefinedとなっています。
const hoge = process.env.WEB_URL || '';
のようにdefault値を用意する手もありますが、今回はこちらで使われている型を拡張していきます。
create-react-appを実行したときに/src
直下にreact-app-env.d.ts
というファイルが追加されています。
こちらに型を書いていきます。
最終的な形はこのようになります。
/// <reference types="react-scripts" /> declare namespace NodeJS { interface ProcessEnv { readonly REACT_APP_WEB_URL: string; } }
prefixとしてREACT_APP_
を環境変数につける必要があります。
.env
の中身は下記のようになります。
REACT_APP_WEB_URL=https://example.com
これで最初に書いた方がstringと推論されるようになりました。
const hoge = process.env.WEB_URL; // const hoge: string;
余談 react-app-env.d.tsについて
react-app-env.d.ts
ですが、init直後はこのようになっていました。
/// <reference types="react-scripts" />
この記述はTypeScriptのTriple-Slash Directivesというもので、
何種類かありますが、今回使用されているのは /// <reference types="..." />
型ですね。
ドキュメントには下記のように書かれています。
a ///
directive declares a dependency on a package. ... For example, including /// in a declaration file declares that this file uses names declared in @types/node/index.d.ts;
今回の例だと、/// <reference types="react-scripts" />
とあるので、react-app-env.d.ts
内で node_modules/react-scripts/lib/react-app.d.tsで宣言されている名前を使用することを表しています。
この記事の最初に記述したようなコードと同じようなことをnode_modules/react-script/lib/react-app.d.ts
の中でも行っていることがわかります。
declare namespace NodeJS { interface ProcessEnv { readonly NODE_ENV: 'development' | 'production' | 'test'; readonly PUBLIC_URL: string; } }
このために、vscodeなどのエディターでは最初から型補完が効いているみたいですね。