express-generatorで作成したプロジェクトでTypeScriptを使用する

今回は、express-generatorでプロジェクトを作成し、一部でTypeScriptを使用できるように環境構築を行います。

あらかじめ、npm、node、express-generatorをインストールしている前提で記載させていただきます。

## 環境
■ Nodeのバージョン
v14.14.0
■ 使用OS
MacOS Big Sur：バージョン11.6
■ 総合開発環境
Visual Studio Code

## プロジェクト作成


```
% express --view=ejs types

   create : types/
   create : types/public/
   create : types/public/javascripts/
   create : types/public/images/
   create : types/public/stylesheets/
   create : types/public/stylesheets/style.css
   create : types/routes/
   create : types/routes/index.js
   create : types/routes/users.js
   create : types/views/
   create : types/views/error.ejs
   create : types/views/index.ejs
   create : types/app.js
   create : types/package.json
   create : types/bin/
   create : types/bin/www

   change directory:
     $ cd types

   install dependencies:
     $ npm install

   run the app:
     $ DEBUG=types:* npm start

```

作成したプロジェクトに移動します。

```
% cd types 
```

### パッケージインストール

Expressに必要なパッケージをインストールします。

```
% npm install
npm notice created a lockfile as package-lock.json. You should commit this file.
added 54 packages from 38 contributors and audited 55 packages in 2.921s
found 0 vulnerabilities
```

typescriptと@types/nodeをインストールします。

```
% npm install -D typescript @types/node
+ @types/node@16.11.6
+ typescript@4.4.4
added 2 packages from 42 contributors and audited 57 packages in 1.379s
found 0 vulnerabilities
```

tsconfig.jsonを生成します。

```
% npx tsc --init
message TS6071: Successfully created a tsconfig.json file.
```

ts-node、ts-node-dev、@types/express、@types/nodeをインストールします。

```
% npm install -D ts-node ts-node-dev @types/express @types/node   
+ @types/node@16.11.6
+ @types/express@4.17.13
+ ts-node@10.4.0
+ ts-node-dev@1.1.8
added 68 packages from 118 contributors, updated 1 package and audited 125 packages in 9.451s

4 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

```

これで必要パッケージをインストールできました。
今回は、「www、app.js、index.js」の拡張子をtsに変更します。
変更後は「www.ts、app.ts、index.ts」になるようにします。

Visual Studio Codeなどの総合開発環境で確認すると、拡張子を変更したことによる型に関するエラーが出力されているので、修正します。

修正前

```index.ts
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {    //型エラー
  res.render('index', { title: 'Express' });
});

module.exports = router;
```

修正後

```index.ts
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req:any, res:any, next:any) {　//ここを修正
  res.render('index', { title: 'Express' });
});

module.exports = router;
```

「www.ts、app.ts」も同様に修正します。

```www.ts
#!/usr/bin/env node

/**
 * Module dependencies.
 */

var app = require('../app');
var debug = require('debug')('types: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:any) {　//ここを修正
  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:any) {　//ここを修正
  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);
}

```

```app.ts
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

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

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

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')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req:any, res:any, next:any) {　//ここを修正
  next(createError(404));
});

// error handler
app.use(function(err:any, req:any, res:any, next:any) {　//ここを修正
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

```

### ts-node-devを使用する

ts-node-devを使用できるように、package.jsonを編集します。

```package.json
"scripts": {
    "start": "node ./bin/www",
    "dev": "ts-node-dev --respawn ./bin/www"   //ここを追記
  },
```

上記により、「npm run dev」コマンドでサーバーを起動できるようになりました。
最後に、サーバーを起動して「localhost:3000/」にアクセスします。

```
% npm run dev

> types@0.0.0 dev /Users/mizumotoshintarou/study/types
> ts-node-dev --respawn ./bin/www

[INFO] 23:08:21 ts-node-dev ver. 1.1.8 (using ts-node ver. 9.1.1, typescript ver. 4.4.4)
GET / 304 22.110 ms - -
GET /stylesheets/style.css 200 23.675 ms - 111
```

Expressの初期画面にアクセスできたかと思います。
Expressのプロジェクト作成時に、全てのファイルをTypeSciptで生成してくれるような機能は、私が検索した限りだと見当たりませんでした。
もし、もっと簡単な方法でTypeScript化できる方法をご存知の方がいらっしゃれば教えてください。

以上です。
ありがとうございました。