In this guide, we will go through the steps to get a Buidler project working with TypeScript. This means that you can write your Buidler config, tasks, scripts and tests in TypeScript. For a general overview of using Buidler refer to the Getting started guide.
Buidler detects if typescript
and ts-node
are installed in its npm project,
and automatically enables TypeScript support.
To install them, open your terminal, go to your Buidler project, and run:
npm install --save-dev ts-node typescript
You also need these packages:
npm install --save-dev chai @types/node @types/mocha @types/chai
Let's get started with a fresh Buidler project. Run npx buidler
and go through the steps to create a sample project. When you're done your project directory should look like this:
$ ls -l
total 400
-rw-r--r-- 1 fzeoli staff 195 Jul 30 15:27 buidler.config.js
drwxr-xr-x 3 fzeoli staff 96 Jul 30 15:27 contracts
drwxr-xr-x 502 fzeoli staff 16064 Jul 30 15:31 node_modules
-rw-r--r-- 1 fzeoli staff 194953 Jul 30 15:31 package-lock.json
-rw-r--r-- 1 fzeoli staff 365 Jul 30 15:31 package.json
drwxr-xr-x 3 fzeoli staff 96 Jul 30 15:27 scripts
drwxr-xr-x 3 fzeoli staff 96 Jul 30 15:27 test
Now we are going to rename the config file from buidler.config.js
to buidler.config.ts
, run:
mv buidler.config.js buidler.config.ts
We also need to adapt it to explicitly import the Buidler config DSL, and use the Buidler Runtime Environment explicitly.
For example, the sample project's config turns from this
usePlugin("@nomiclabs/buidler-waffle");
// This is a sample Buidler task. To learn how to create your own go to
// https://buidler.dev/guides/create-task.html
task("accounts", "Prints the list of accounts", async () => {
const accounts = await ethers.getSigners();
for (const account of accounts) {
console.log(await account.getAddress());
}
});
module.exports = {};
into this
import { task, usePlugin } from "@nomiclabs/buidler/config";
usePlugin("@nomiclabs/buidler-waffle");
// This is a sample Buidler task. To learn how to create your own go to
// https://buidler.dev/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, bre) => {
const accounts = await bre.ethers.getSigners();
for (const account of accounts) {
console.log(await account.getAddress());
}
});
export default {};
Next, create a file tsconfig.json
in your project directory and put the following in it:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "dist"
},
"include": ["./scripts", "./test"],
"files": [
"./buidler.config.ts"
]
}
And that's really all it takes. Now the configuration file will be run as TypeScript.
One of the advantages of using TypeScript, is that you can have an type-safe configuration, and avoid typos and other common errors.
To do that, you have to write your config in TypeScript in this way:
import { BuidlerConfig } from "@nomiclabs/buidler/config";
const config: BuidlerConfig = {
// Your type-safe config goes here
};
export default config;
Some Buidler plugins, like buidler-waffle and buidler-ethers, add new properties to the Buidler Runtime Environment. To keep everything type-safe and make using them with TypeScript possible, they provide type extension files.
For these to be taken into account, you'll need to add the type extension files to the files
field in your tsconfig.json
, like this:
"files": [
"./buidler.config.ts",
"./node_modules/@nomiclabs/buidler-ethers/src/type-extensions.d.ts",
"./node_modules/@nomiclabs/buidler-waffle/src/type-extensions.d.ts"
]
Alternatively, if for whatever reason you can't import the type extension files from node_modules, you can use the triple-slash directive.
Create a new file called buidler-env.d.ts
and write this inside:
/// <reference types="@nomiclabs/buidler-ethers/src/type-extensions" />
/// <reference types="@nomiclabs/buidler-waffle/src/type-extensions" />
You shouldn't need to modify your tsconfig.json
this time, because these are .d.ts
file and should be included by the compiler, but here's how you can do it, in case you have to:
"files": [
"./buidler.config.ts",
"./buidler-env.d.ts",
]
Plugins that include type extensions should have documentation detailing their existance and the path to the type extension file.
To write your smart contract tests and scripts you'll most likely need access to an Ethereum library to interact with your smart contracts. This will probably be one of buidler-ethers or buidler-web3, all of which inject instances into the Buidler Runtime Environment.
When using JavaScript, all the properties in the BRE are injected into the global scope, and are also available by getting the BRE explicitly. When using TypeScript nothing will be available in the global scope and you will need to import everything explicitly.
An example for tests:
import { ethers } from "@nomiclabs/buidler";
import { Signer } from "ethers";
describe("Token", function() {
let accounts: Signer[];
beforeEach(async function() {
accounts = await ethers.getSigners();
});
it("should do something right", async function() {
// Do something with the accounts
});
});
An example for scripts:
import { run, ethers } from "@nomiclabs/buidler";
async function main() {
await run("compile");
const accounts = await ethers.getSigners();
console.log("Accounts:", accounts);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Under the hood, Buidler uses ts-node to support TypeScript. By default, it will recompile and type-check everything on every run. Depending on your project's size, this can get slow.
You can make Buidler run faster by preventing ts-node
from type-checking your project. This is done by setting the
TS_NODE_TRANSPILE_ONLY
en variable to 1
. For example, you can run your TypeScript-based tests faster like this
TS_NODE_TRANSPILE_ONLY=1 npx buidler test
.
ts-node
supportWhen running Buidler scripts without the CLI, you need to use ts-node
's --files
flag.
This can also be enabled with TS_NODE_FILES=true
.
To use Buidler with TypeScript you need to be able to import Buidler from your project to access the Buidler Runtime Environment, and this wouldn't be possible with a global installation. Because of this Buidler only supports TypeScript on local installations.