ASP.NET Core Blazor 向けに TypeScript で開発する方法

2024/04/27に公開

はじめに

ASP.NET Core Blazor で色々開発していくと、どうしても JavaScript でやりたいということが出てくると思います。
その場合に TypeScript でなるべく開発する方法について書いていこうと思います。

Razor コンポーネントに紐づく TypeScript ファイルを作成する方法

まずは、Razor コンポーネントに紐づく JavaScript ファイルを使う方法から説明します。
普通は Components/Pages/Home.razor という Razor コンポーネントに対して Components/Pages/Home.razor.js という JavaScript ファイルを作成します。
これを Components/Pages/Home.razor.ts という TypeScript で開発できるようにしたいと思います。

TypeScript コンパイラの追加

Visual Studio で TypeScript をコンパイルする方法として NuGet パッケージを追加する方法があります。
以下のパッケージを追加することで TypeScript がコンパイルされるようになります。

  • Microsoft.TypeScript.MSBuild

そしてプロジェクト直下に tsconfig.json を追加します。

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "removeComments": false,
    "skipLibCheck": true,
    "strict": true,
    "sourceMap": true,
    "noImplicitAny": true
  },
  "include": [
    "Components/**/*.ts"
  ]
}

こうすることで、Components/Pages/Home.razor.ts という TypeScript ファイルを作成して開発すると、JavaScript がコンパイルされて生成されるようになります。

例えば Components/Pages.Home.razor.ts という名前で以下のような内容の TypeScript ファイルを作成します。

Components/Pages/Home.razor.ts
export function alertMessage(message: string): void {
    alert(message);
}

そうすると以下のように JavaScript が生成されます。

では、実際にこの alertMessage 関数を Razor コンポーネントから呼び出してみましょう。

Components/Pages/Home.razor
@page "/"
@rendermode InteractiveServer
@implements IAsyncDisposable
@inject IJSRuntime JSRuntime

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<button @onclick="OnClickAsync">Click me</button>

@code {
    private IJSObjectReference? _module;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _module = await JSRuntime.InvokeAsync<IJSObjectReference>(
                "import",
                "./Components/Pages/Home.razor.js");
        }
    }

    private async Task OnClickAsync()
    {
        if (_module != null)
        {
            await _module.InvokeVoidAsync(
                "alertMessage", 
                "Hello from Blazor!");
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (_module != null)
        {
            await _module.DisposeAsync();
        }
    }
}

このような感じで IJSRuntime を使って import 関数を呼び出して JavaScript ファイルを読み込んで、その中の関数を呼び出すことができます。

ただ、やっておいて何ですが Razor コンポーネントに紐づく JavaScript は、そこまで巨大なものを作ることも無いと思うので、ぶっちゃけ JavaScript で書いちゃってもいいと思います。

課題

TypeScript 弱者なので tsconfig.json に書いたオプションが今回のようなブラウザーで使う用途として最適かどうかわかってないので、もし最新のもダウンブラウザーを対象にする場合にはこうしたほうがいいよ!!等があれば教えて頂けると嬉しいです。

Razor クラスライブラリで JavaScript イニシャライザーを TypeScript で開発する方法

次は、Raozr クラスライブラリなどで使う JavaScript イニシャライザーを TypeScript で開発する方法について説明します。
JavaScript イニシャライザーについては、以下の記事を参考にしてください。

https://zenn.dev/okazuki/articles/how-to-load-js-onstartup

JavaScript イニシャライザーは、もしかしたら結構大きなコードを書くことになるかもしれないので、TypeScript で開発したいという要望があるかもしれません。
そういう時に使える方法を紹介します。

Razor クラスライブラリと JavaScript プロジェクトの作成

Razor クラスライブラリ プロジェクトを作成をして、その他に Blank TypeScript Project を作成します。
Blank TypeScript Project で開発したビルド成果物の JavaScript を Razor クラスライブラリ プロジェクトの JavaScript イニシャライザーとして使います。

esbuild を使った TypeScript ビルドをするように構成する

まず npm install を使って以下の 2 つのパッケージを devDependencies として追加します。

  • esbuild
  • rimraf

ちなみに npm install コマンドを使わなくても Visual Studio 2022 から GUI でやることも出来ます。便利…!

やりかたは Blank TypeScript Project の下にある npm ノードの右クリックメニューで「新しい npm パッケージのインストール」を選択します。

そうすると以下のような画面が表示されるので、そこからパッケージ名で検索したり、どの依存関係に追加するのかと画面でポチポチとやることで追加できます。ちょっとこれだけで Visual Studio 2022 のほうを TypeScript/JavaScript 開発するのに使っていいかも…って思ったかも…。

そして、esbuild.config.js という名前で以下の内容のファイルを追加します。

esbuild.config.js
import * as esbuild from 'esbuild'
import pkg from './package.json' assert { type: 'json' }

await esbuild.build({
    entryPoints: [ pkg.source ],
    bundle: true,
    minify: true,
    sourcemap: true,
    target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
    outfile: pkg.main,
})

あとは、これを使ってビルドをするように package.json を以下のように変更します。
変更した箇所は sourcemainscripts の部分です。main の所のファイル名は Razor クラスライブラリ プロジェクトの名前に合わせてください。今回は RazorClassLibrary1 という名前のプロジェクトなので dist/RazorClassLibrary1.lib.module.js という名前にしています。

package.json
{
  "name": "nodeconsoleapp1",
  "version": "1.0.0",
  "description": "",
  "source": "src/index.ts",
  "main": "dist/RazorClassLibrary1.lib.module.js",
  "scripts": {
    "build": "node ./esbuild.config.mjs",
    "clean": "rimraf ./dist"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20.12.7",
    "@typescript-eslint/eslint-plugin": "^7.7.1",
    "@typescript-eslint/parser": "^7.7.1",
    "esbuild": "^0.20.2",
    "eslint": "^8.57.0",
    "rimraf": "^5.0.5",
    "typescript": "^5.4.5"
  }
}

src フォルダーで開発する想定なので tsconfig.json"rootDirs": ["./src"], を追加します。

そして Blank TypeScript Project のプロジェクトファイル (ここではデフォルト名で作ったので NodeConsoleApp1.esproj になっています) に DebugAssetsDirectoryStaticWebAssetSourceId を追加します。DebugAssetsDirectory はビルドした JavaScript の成果物を配置するディレクトリを指定します。StaticWebAssetSourceId は Razor クラスライブラリ プロジェクトの名前を指定します。

NodeConsoleApp1.esproj
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
  <PropertyGroup>
    <DebugAssetsDirectory>dist\</DebugAssetsDirectory>
    <StaticWebAssetSourceId>RazorClassLibrary1</StaticWebAssetSourceId>
  </PropertyGroup>
</Project>

これで TypeScript ファイルを開発してビルドするように構成ができました。
src/index.ts という名前のファイルをビルドするようになっているので、ここから色々なファイルを import したりして開発をするといい感じにビルドされて dist/RazorClassLibrary1.lib.module.js という名前のファイルが生成されるようになります。

Razor クラスライブラリ プロジェクトの構成

JavaScript ファイルが生成されるようになったので、Razor クラスライブラリ プロジェクトから Blank TypeScript Project を参照に追加します。

これで Blank TypeScript Project を参照に追加することで、そこで開発された JavaScript ファイルが自動的に JavaScript イニシャライザーとして読み込まれるようになります。

動かしてみよう

ということで本当に読み込まれているのか確認してみましょう。Blank TypeScript Project の src フォルダーに mymoudle.tsindex.ts を作って以下のように変更します。

src/mymodule.ts
export function alertFromMyModule(message: string): void {
    alert(`alertFromMyModule(${message})`);
}
src/index.ts
import { alertFromMyModule } from "./mymodule";

alertFromMyModule("Hello from index.ts");

こうすると JavaScript ファイルが読み込まれていると alertFromMyModule(Hello from index.ts) というアラートが表示されるはずです。

Blazor Web App プロジェクトに Razor クラスライブラリ プロジェクトを参照に追加してデバッグ実行してみましょう。そうすると以下のように表示されるはずです…!

これで本格的な TypeScript で JavaScript イニシャライザーを開発できます。

まとめ

ということで ASP.NET Core Blazor を使って開発する際に JavaScript を書かないといけない場合でも TypeScript で開発する方法について紹介しました。
Razor コンポーネントに紐づく JavaScript の開発のほうでは他のファイルを import したりしていい感じに開発する方法がわからなかったので、もしどなたかもっといい方法をご存知の方がいたら教えてください。

ということでタイプセーフな開発を楽しんでください!

Microsoft (有志)

Discussion