One of the most exciting prospects of Blazor is the potential to remove the need for JavaScript. However, we are not there yet. In an earlier post, I pointed out that WebAssembly isn't currently able to interact with the DOM or call browser APIs. I'm not even sure how server-side Blazor is going to move away from JavaScript or if it can.

So if we're going to have to write JavaScript, then it would be great to get as close to our development experience with C# as we can. This is where we can leverage TypeScript.

TypeScript is a first class citizen in Visual Studio, one of the benefits of this is that any TypeScript you write in your Blazor project will be transpiled to JS for you automatically.

But what about TypeScript in a Blazor library project? Unfortunately, this doesn't get compiled automatically. It turns out though that it's not too difficult to get this working.

What is TypeScript

Just before we continue, for those who aren't familiar, TypeScript is a typed superset of JavaScript, which compiles to plain JavaScript. It gives us the ability to use static typing, classes and interfaces. But the biggest benefit is that because it's compiled, we can get compile time checks on our code as apposed to writing plain JS where errors may only show up at runtime.

The Example

For this post we're going to use the standard Blazor library template. You can create this via the dotnet CLI using the following command.

dotnet new blazorlib

If you don't have this template available you can install it using this command.

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::0.9.0-preview3-19154-02

NOTE: You may have to adjust the version at the end, depending on when you read this.

Inside this project there is a content folder with a file called exampleJsInterop.js, with the following contents.

// This file is to show how a library package may provide JavaScript interop features
// wrapped in a .NET API

window.exampleJsFunctions = {
  showPrompt: function (message) {
    return prompt(message, 'Type anything here');
  }
};

We're going to convert this file to TypeScript and then make a few changes to the project to make it compile when the project builds.

Converting to TypeScript

As I mentioned earlier, TypeScript is a superset of JavaScript what this means is that any valid JavaScript is valid TypeScript. So we could just give the exampleJsInterop file a .ts extension and we would be done. But that kind of defeats the point of using TypeScript.

So we're going to rewrite the code to take advantage of some of the features TypeScript gives us.

// This file is to show how a library package may provide JavaScript interop features
// wrapped in a .NET API

namespace JSInteropWithTypeScript {

    class ExampleJsFunctions {
        public showPrompt(message: string): string {
            return prompt(message, 'Type anything here');
        }
    }

    export function Load(): void {
        window['exampleJsFunctions'] = new ExampleJsFunctions();
    }
}

JSInteropWithTypeScript.Load();

This is how things look once converted to TypeScript. Hopefully, this should look a lot more familiar to C# developers. We now have a namespace, a class and types. I'll admit that types don't really give us much as this code is going to be called by C#. But if the showPrompt method was going to be called by another TypeScript method we would now benefit from compile time checks.

Configuring the build

We now have our TypeScript file so how can we get it to build with our project. The first thing we need to do is to install the Microsoft.TypeScript.MSBuild package from NuGet. You can do this either via the NuGet package manger.

Install-Package Microsoft.TypeScript.MSBuild

Or via the dotnet CLI.

dotnet add package Microsoft.TypeScript.MSBuild

Once installed, we need to edit the .csproj file. We're going to add in a TypeScriptToolsVersion tag in the PropertyGroup node.

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <IsPackable>true</IsPackable>
    <RestoreAdditionalProjectSources>
      https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json;
      https://dotnet.myget.org/F/blazor-dev/api/v3/index.json;
    </RestoreAdditionalProjectSources>
    <LangVersion>7.3</LangVersion>
    <TypeScriptToolsVersion>3.2</TypeScriptToolsVersion>
  </PropertyGroup>

This tells MSBuild which version of TypeScript to use when compiling the project. If this is not set then the build will use the latest version installed on the system.

We need to tell MSBuild what files it should build. In order to do this we're going to use the TypeScriptCompile item type.

<ItemGroup>
  <TypeScriptCompile Include="content/exampleJsInterop.ts" />
</ItemGroup>

Checking the build

That should be all we need to be able to compile our TypeScript. We can now do a build and if everything has gone to plan then you should see a exampleJsInterop.js file and a exampleJsInterop.js.map file.

The .map file has been generated for us by the TypeScript compiler. Map files provide a mapping between the original TypeScript source file and the compiled JavaScript. This means we can debug the TypeScript version of our code in the browser instead of the compiled JavaScript version.

Summary

In this post, we've taken a first look at how we can use TypeScript with our Blazor libraries. As well as how we can configure our projects to compile our TypeScript files during a build.

There is a lot more that can be done with TypeScript in terms of configuration but I wanted to provide a quick start guide which should work for most interop cases in Blazor. Have you written much interop code so far? Let me know in the comments.