Adding Typescript to Truffle and Buidler

How to use TypeChain to utilize the powers of Typescript in your project

Unlike compiled languages, you pretty much have no safeguards when running JavaScript code. You'll only notice errors during runtime and you won't get autocompletion during coding.

typescript

With Typescript you can get proper typechecking as long as the used library exports its types. Most Ethereum developer libraries (and most major libraries in general) are luckily exporting type definitions which makes using Typescript a breeze.

But what about your contracts itself? And how does a good setup for Truffle/Buidler with Typescript in general look like? Let's have a look.

Integrating it into Truffle and Buidler

We will use the Truffle MetaCoin box as starting point once again, but the general process should work for any up-to-date Truffle/Buidler project.

$ truffle unbox MetaCoin
$ npm init

Let's use the latest Solidity 0.7.0 and now add our required dependencies:

$ npm install truffle typescript ts-node typechain @typechain/truffle-v5 --save-dev
$ npm install @types/node @types/mocha @types/chai --save-dev

If you're not using Truffle v5, you will have to change the installation here from @typechain/truffle-v5 to the correct target. Available targets are

  • truffle-v4
  • truffle-v5
  • ethers-v4
  • ethers-v5
  • web3-v1

Typescript Setup

Let's add a tsconfig.json to the root folder of our project. This is essentially the configuration for Typescript. We don't have to worry about backwards compatibilty in our contracts repo, so we can use ES2018. And you also want to add the types for node and the testing library:

{
  "compilerOptions": {
    "lib": ["ES2018"],
    "module": "CommonJS",
    "target": "ES2018",
    "strict": true,
    "esModuleInterop": true,
    "types": ["@types/node", "@types/chai", "@types/mocha"],
    "typeRoots": ["./node_modules/@types", "./types"]
  },
  "include": ["**/*.ts"],
  "exclude": ["node_modules", "build", "cache", "artifacts"]
}

Truffle Configuration

Now we have to register ts-node in our truffle-config.js. This will be required, so we can run migration files written in TypeScript.

Add this to the top of the truffle-config.js:

require("ts-node").register({
  files: true,
});

Now let's rename all .js => .ts files inside /test. Depending on your test files, you might have to add a few type changes. In our MetaCoin example, the only change required is adding a type for the passed accounts like this: accounts: string[]. If you use something like VS Code (much recommended), you should automatically see missing types in your IDE.

Now off to our migrations. Truffle in theory had a feature added to also look for .ts files, but this didn't work for me currently. Instead we can simply create a separate deploy_contracts.ts file and require this one in our 2_deploy_contracts.js.

2_deploy_contracts.js:

module.exports = require("./deploy_contracts")(artifacts, web3);

And the respective deploy_contracts.ts will look like this:

type Network = "development" | "kovan" | "mainnet";

module.exports = (artifacts: Truffle.Artifacts, web3: Web3) => {
  return async (
    deployer: Truffle.Deployer,
    network: Network,
    accounts: string[]
  ) => {
    const ConvertLib = artifacts.require("ConvertLib");
    const MetaCoin = artifacts.require("MetaCoin");

    await deployer.deploy(ConvertLib);
    await deployer.link(ConvertLib, MetaCoin);
    await deployer.deploy(MetaCoin);

    const metaCoin = await MetaCoin.deployed();
    console.log(
      `Metacoin deployed at ${metaCoin.address} in network: ${network}.`
    );
  };
};

TypeChain Configuration

Now let's configure TypeChain to actually generate us our types. It's as simple as adding those package.json scripts:

"scripts": {
    "generate-types": "npx typechain --target=truffle-v5 'build/contracts/*.json'",
    "postinstall": "npx truffle compile && npm run generate-types"
}

Once again, if you don't use Truffle v5, change the target here accordingly. Now do a simple npm install and every time you run this command again, it will also compile and generate the contracts types for you.

finished

Your contracts should now automatically be type-checked in your tests. You may also want to use the generated types in your frontend. Make sure to use the correct ethers or Web3 target in this case.

Buidler Configuration

Buidler has great Typescript support. Now with the above setup, you really don't have to add anything more. If you only want to use Buidler, you may even want to take a shortcut via the buidler-typechain plugin. However the plugin merely saves you the task of adding the generate type commands from above, so you might as well do this as described above.

Don't forget to git-ignore generated types

As a general rule, you don't want to commit auto-generated files (except for lock files of course) into Git. So add a .gitignore file like this:

# truffle
build

# typechain
types

# buidler
artifacts
cache

Happy typescripting!

That's all. The setup is pretty easy. You may run into issues with certain libraries not having declared types or having to learn Typescript, but it's really worth it. Usually the bit of extra struggle is easily offset by detecting errors while writing them.

And you always have the any type to essentially turn off type-checking, but avoid it if possible and good luck on your Typescript journey.


Markus Waas

Solidity Developer

More great blog posts from Markus Waas

© 2024 Solidity Dev Studio. All rights reserved.

This website is powered by Scrivito, the next generation React CMS.