Edited at

TypescriptでExpressを設定する


課題意識

バックエンドをExpressで構築したくなった時、Typescriptに対応するように設定するのが少し困難でした。

一応Typescript-Node-StarterというMicrosoftのスタートアップアプリケーションもありますが、Mongodbなどを利用したくない場合はこれでは使えません。

なので、軽量なExpressのCLIによるスタートアッププロジェクトをベースにTypescriptに対応させる設定方法を解説します。


エクスプレスサーバーを立ち上げ

今回はバックのみなのでviewを省略。

express --git --no-view


Typescriptのインストール

npm install typescript


@types/node, @Types/expressのインストール

Expressをコンパイルできるようにnodeの型定義とexpressの型定義をインストール。

npm install @types/node

npm install @types/express


コンパイルの設定

tsconfigの"target"オプションを"es6"にしときます。

また、"outDir"オプションを "./dist"にしとくと見通しがよくなります。

他のオプションはデフォルトで。


tsconfig.json

{

"compilerOptions": {
"target": "es6",
"outDir": "./dist",
.....
}
}


.gitignoreファイルの修正

.gitignoredistの表記を追加しましょう


.gitignore

dist

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.....


不要ファイルの削除

以下のファイルはバックエンドには不要なので削除

rm -r public/javascripts

rm -r public/stylesheets
rm -r public/index.html
rm routes/index.js


.jsファイルを全て.tsファイルに変更

拡張子を.tsに変更していきます。

mv users.js users.ts

mv app.js app.ts
mv www www.ts


users.tsの修正

このファイルは削除しても良いですが、Typescriptに書き換える場合は以下の手順が必要です。

1. varconstに書き換える。

2. exports.module = routerの記述はexport {router}に書き換えてTypescriptのモジュールシステムを利用。

3. req, res, nextに型を付与。


users.ts

import {NextFunction, Request, Response} from "express";

const express = require('express');
const router = express.Router();

/* GET users listing. */
router.get('/', (req: Request, res: Response, next: NextFunction) {
res.send('respond with a resource');
});

export {router}



app.ts

const express = require('express');

const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

const app = express();

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
const p = new Promise(() => {
});
app.use('/', indexRouter);
app.use('/users', usersRouter);

export {app}



app.tsの修正

以下の点を修正します。

1. indexRouterの削除

2. user.tsの変更に応じてrouterのimport方法をTypescriptに対応

3. varconstに変更

4. appをtypescriptを利用してexport


app.ts

import {NextFunction, Request, Response} from "express";

const express = require('express');
const router = express.Router();

/* GET users listing. */
router.get('/', (req: Request, res: Response, next: NextFunction) => {
res.send('respond with a resource');
});

export {router}



www.tsの修正

以下の記述を削除

var app = require('../app');

代わりに以下の記述を追加

import {app} from "../app";

修正結果


www.ts

#!/usr/bin/env node


import {app} from "../app";

/**
* Module dependencies.
*/

var debug = require('debug')('express-typescript:server');
var http = require('http');

/**
* Get port from environment and store in Express.
*/

var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);

/**
* Create HTTP server.
*/

var server = http.createServer(app);

/**
* Listen on provided port, on all network interfaces.
*/

server.listen(port);
server.on('error', onError);
server.on('listening', onListening);

/**
* Normalize a port into a number, string, or false.
*/

function normalizePort(val) {
var port = parseInt(val, 10);

if (isNaN(port)) {
// named pipe
return val;
}

if (port >= 0) {
// port number
return port;
}

return false;
}

/**
* Event listener for HTTP server "error" event.
*/

function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}

var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;

// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}

/**
* Event listener for HTTP server "listening" event.
*/

function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}



自動サーバー再起動のインストール (任意)

開発中は「修正 - コンパイル - 起動」のプロセスを自動化したいです。

そのために自動再起動ツールnodemon(nodeとdemonをくっつけた言葉)をinstallします。

npm install nodemon


tsconfig.jsonの修正

"scripts"オプションを修正します。

修正内容は以下の点です。


  1. 自動コンパイルスクリプト追加

  2. 自動再起動スクリプト(nodemon追加

  3. スタートスクリプトの修正


tsconfig.json

{

"name": "express-typescript",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./dist/bin/www.js",
"nodemon": "nodemon ./dist/bin/www.js",
"build": "tsc",
"watch": "tsc -w"
},
"dependencies": {
"@types/node": "^10.12.10",
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"jasmine": "^3.3.0",
"morgan": "~1.9.0",
"nodemon": "^1.18.6",
"typescript": "^3.1.6"
}
}


コンパイル&起動してみよう

terminalを二つ開いて以下の2コマンドでコンパイルとサーバーの起動を自動化しましょう。


terminal2

npm run nodemon



terminal2

npm run watch



最終確認

localhost:3000/usersにアクセスして繋がれば完成です。


終わりに

お疲れ様でした。これで基礎的な設定は完了です。

結構長かったので、毎回この作業をするのは面倒だと思うかもしれません。

でも心配しなくて大丈夫です。ここまでをbitbucketにあげておきました。

https://bitbucket.org/kubo0725/express-typescript/src/master/

(初めて公開するので公開方法あってるか不安)

読んでいただきありがとうございました。