typescript

Create bots with TypeScript

Whenever I’m doing demos or other presentations I work very hard to keep things as real world as possible. While there will certainly be little “cheats”, such as keeping all items in a single file, or having snippets already available, to help make the demo easier to digest, there’s one big lie I wind up telling every bot presentation I do. I do all my demos in JavaScript, even though I create all my bots using TypeScript.

Why the lie?

Whenever I’m talking about how to build a chat bot using Bot Framework, I’m trying to demonstrate how you can do so using skills you already have. If you know JavaScript, and have even a cursory understanding of Node.js, you can get up and running relatively quickly with your first bot. The last thing in the world I want to do is throw another hurdle in front of attendees, like learning another programming language.

Even though I don’t want to put another hurdle in front of someone, the thing about TypeScript is it turns out it’s not much of a hurdle after all.

What is TypeScript

If you’re not already familiar with TypeScript, it’s a language headed by Anders Hejlsberg, whose claims to fame include Delphi and C#. TypeScript is designed to help overcome the various shortcomings of JavaScript, and do so in such a fashion that allows you to begin using cutting edge features, such as async/await, which aren’t globally available in ECMAScript.

TypeScript is designed to build on the existing syntax of JavaScript; in fact, 70% of the syntax in TypeScript is simply JavaScript. This helps make the transition to the language smoother for experienced JavaScript developers. In a lot of ways, TypeScript is a combination of Java and JavaScript.

TypeScript offers many features you expect from programming languages, such as static (or strong) typing, OOP, and better module management.

One key about TypeScript is it transcompiles into JavaScript, and to the version of JavaScript/ECMAScript you need. So you can write in TypeScript, take advantage of the features the language offers, knowing the resulting code will run in the environment you’re targeting.

Why use TypeScript when creating bots?

One of the biggest weaknesses of JavaScript is the inability to declare the type of a variable. This limits the amount of support an IDE, such as VS Code, can offer you. Below is the code one might have in a separate file for a dialog in a bot:

1
2
3
4
5
module.exports = [
(session) => {
session.endConversation('Thank you for your input!');
}
]

If you put that into a JavaScript file, you would notice when hitting dot after session, VS Code wouldn’t show you endConversation as an available option, or if it did, it’s because it’s seeing it from another file in your project. The IDE has no way of knowing that session is actually of type builder.session.

Contrast that with the following bit of TypeScript, where we are able to identify the type:

1
2
3
4
5
export default [
(session: builder.Session) => {
session.endConversation('Thank you for your input!');
}
]

When you create the TypeScript file, you’ll notice VS Code knows exactly what the session parameter is, and is able to offer you IntelliSense.

Getting started with TypeScript

Installing TypeScript

In order to start programming in TypeScript, you will need to install TypeScript. TypeScript is available as an NPM package, and you can simply add it as a developer dependency to your project. Personally, because I use TypeScript extensively, I install it globally.

1
npm install -g typescript

Creating and configuring the project

If you use the Bot Framework Yeoman generator I created you will notice there is a template already available for TypeScript. For purposes of this post, we’ll create everything from scratch, so you can see how it is all brought together.

Add packages and dependencies

Create a new folder, and add a package.json file with the following contents:

1
2
3
4
5
6
7
8
9
10
11
{
"name": "type-script-bot",
"dependencies": {
"botbuilder": "^3.4.4",
"restify": "^4.3.0"
},
"devDependencies": {
"@types/node": "^6.0.52",
"@types/restify": "^2.0.35"
}
}

The dependencies section is pretty standard for a bot, but you might not be familiar with the devDependencies. The devDependencies contain the types of the various packages we’ll be using. Types are the various interfaces for the objects and classes in a particular package. So @types/restify contains the interfaces provided by restify. This will add IntelliSense to the project. In the case of botbuilder, we don’t need to add a types file, as the framework is written in TypeScript, and contains all of the necessary types. After saving the file, run the installation process like normal.

1
npm install

Configuring TypeScript compilation

As mentioned before, TypeScript will be transcompiled to JavaScript. You configure how this occurs by using a tsconfig.json file.

The module and target options tell the transcompiler to emit JavaScript that is compliant with ES6. outDir specifies where the JavaScript files will be output. The files section identifies which files will be transcompiled. Add a file named tsconfig.json with the following content.

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "./built"
},
"files": [
"dialog.ts",
"app.ts"
]
}

Creating the bot and dialog

Creating the dialog

Let’s start by creating a basic dialog. Add a file to your project named dialog.ts, and add the code you see below. You will notice this is standard Node.js bot code, with a couple of differences.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// dialog.ts
import * as builder from 'botbuilder';
interface IResults {
response: string;
}
export default [
(session: builder.Session) => {
builder.Prompts.text(session, 'What is your name?');
},
(session: builder.Session, results: IResults) => {
session.endConversation(`Hello, ${results.response}`);
}
]

From the top, you’ll notice the import statement is different than in JavaScript. Rather than using require, you use the import command. The * means you’ll be importing everything from the package, and as allows you to identify an alias. The end result is effectively the same as using const builder = require('botbuilder');, like you would have done traditionally.

Second, you’ll notice the creation of the interface IResults. You added a single property named response, and marked it as type string. Interfaces, just as in C# or Java, give you the ability to identify the structure. In the case of TypeScript, interfaces are completely weightless; they are not compiled to the JavaScript. Interfaces help give you a better development experience.

Rather than using module.exports to export the array that contains your waterfall, you use export default, followed by the array. The syntax is slightly different, but the results are the same.

Finally, you’re declaring the data type of session and results on each of the waterfall steps to aid your development experience. results is using the interface you created earlier in the file. You’ll notice when you do this, and you start typing response.results, VS Code will provide IntelliSense, and show you response as an available property of type string.

Creating the bot and host

The code to create the bot will be similar to what you would typically do with JavaScript. The main difference you’ll notice is the way packages and other items are imported. Add a file named app.ts with the following code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// app.ts
import * as restify from 'restify';
import * as builder from 'botbuilder';
import dialog from './dialog'
const connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
const bot = new builder.UniversalBot(connector, dialog);
const server = restify.createServer();
server.post('/api/messages', (bot.connector('*') as builder.ChatConnector).listen());
server.listen(process.env.PORT, () => console.log(`${server.name} listening to ${server.url}`));

As you enter the code, you’ll notice that VS Code is able to offer you support throughout the entire process. Because every variable has a type that VS Code can identify, it’s able to provide IntelliSense, unlike when using JavaScript, where the level of support will vary.

Running the code

In order to run the code, it will need to be transcompiled. You can do this by simply running tsc. Because we created a tsconfig.json file, the transcompiler will know what to transcompile, and how to do it. The watch switch will automatically detect changes to the TypeScript files, and transcompile on the fly.

1
tsc --watch

If you take a look at the JavaScript files, you’ll notice they’re relatively similar to what was created in TypeScript. A couple of key differences you’ll notice is the interface we created wasn’t emitted, and the type declarations for parameters are also not part of the JavaScript file that was created. Running the bot is done just as you normally would, using either node or nodemon.

1
node app.js

Next steps

There’s quite a bit that I left on the table when it comes to TypeScript. We could make better use of interfaces with our dialogs, we could create a class for our app, we could …, we could …. My goal with this post was to help get you up and running with TypeScript, and show some of the power that’s made available.

If you want to know more about TypeScript, you can check out the official site, or an edX course.