Deno で import "npm:react" したときに型を扱えるように
Deno Advent Calendar 2024 10 日目の記事です
「Monaco Editor で https インポートしたものモジュールの型を利用できるように」について書くつもりでしたが, 考慮するところが多くまとまらなかったため. Deno で React を使う上で遭遇した問題と対処法について書きます
npm:react だけでは 型定義が利用できない問題
Deno では npm のパッケージを利用するときに npm プロトコルで指定して import することができます
import React, { useState } from "npm:react";
export const App = () => {
const [state, setState] = useState(false);
return <div>{state}</div>;
};
しかし 一部のnpmパッケージでは型が読み取れず, エラーになってしまう場合がよくあります
この例では, setState
を利用して作成した変数の型がany
になってしまっています
npm:@types/react を利用して解決する方法
npmパッケージ自体に型定義を含めていない場合は, このように npm:@types/パッケージ名
を利用する方法があります
// @ts-types="npm:@types/react"
import React, { useState } from "npm:react";
昔は // @ts-types=
ではなく // @deno-types=
でしたが, いつのまにか変わったようですね. deno の名前が消えたので, 共通で使えるようにしたいのでしょうか? Deno以外でこの// @ts-types=
を利用するツールは見つけられませんでした
esm.sh を利用して解決する方法
// @ts-types="https://esm.sh/react@19.0.0"
import React, { useState } from "npm:react";
esm.shの場合, 対応する @types/
のパッケージを指定することなく型定義を利用することができます
Denoの場合, 対応する@types/
のパッケージを自動的に利用しません. 型定義が同一のバージョンで提供されている保証がないため. Deno では暗黙の@types/
パッケージの利用はしないそうです
@types/パッケージ名 で提供されるこの仕組みは分かりづらいため, Denoが期待するようにパッケージ自体に型定義を含めて欲しいですね. なぜ react パッケージに型定義を含めないのだろうか
bun のように npm:
をつけなくても npmパッケージだと解釈し動かす. そんな短絡的な実装をしないDenoらしい選択ですね
[番外] esm.sh で jsr を利用する方法
jsrパッケージ をjsr:
でインポートしたときに 型定義エラーになることはありえないため利用することはあまり利用することがありませんが, npm と同様に 対応するesm.shのURLがあります
import {
encodeBase64 as encodeBase64FromJsr,
encodeBase64Url as encodeBase64UrlFromJsr,
} from "jsr:@std/encoding";
import {
encodeBase64 as encodeBase64FromEsm,
encodeBase64Url as encodeBase64UrlFromEsm,
} from "https://esm.sh/jsr/@std/encoding@1.0.5";
https://esm.sh/jsr/
の次にjsrのパッケージ名ですね
@
でバージョンを指定しているのは, 指定しないと青い波線が表示されるからですね. 今は deno.lock
ファイルでリダイレクトがどう行われたかを残しているので, 指定しなくても良いと思いますが, 波線があると気になるのでバージョン名を指定しています. https://esm.sh/react@19.0.0
も同様です
esm.sh のドキュメントどこにも書かれてる場所を見つけられませんでしたが, ここに jsr のパスを解釈するコードがあるのを見つけました
ありがたいことに, 型定義ファイルもHTTPレスポンスヘッダーで Access-Control-Allow-Origin: *
を指定してくれているので, CORSエラーを気にすることなく, クライアントから直接型定義を取得して活用できます
今度は逆に, esm.sh の方が型定義を一部理解できていないようでした. https://esm.sh/v135/@jsr/std__encoding@1.0.5/_dist/base64url.d.ts が意図せず空になっています
Check file:///Users/narumi/Documents/GitHub/my-repo/main.ts
error: TS2724 [ERROR]: '"https://esm.sh/v135/@jsr/std__encoding@1.0.5/_dist/mod.d.ts"' has no exported member named 'encodeBase64Url'. Did you mean 'encodeBase64'?
encodeBase64Url as encodeBase64UrlFromEsm,
~~~~~~~~~~~~~~~
at file:///Users/narumi/Documents/GitHub/my-repo/main.ts:7:3
'encodeBase64' is declared here.
*/ export declare function encodeBase64(data: ArrayBuffer | Uint8Array | string): string;
~~~~~~~~~~~~
at https://esm.sh/v135/@jsr/std__encoding@1.0.5/_dist/base64.d.ts:16:29
TypeScriptの型定義を対応漏れなく扱うのは難しいですね. esm.sh の Issues で報告しました
Discussion
ts-typesについては、 https://github.com/denoland/deno/issues/27309などが参考になりました。