diff --git a/README.md b/README.md
index ca18976e..11a5e33a 100644
--- a/README.md
+++ b/README.md
@@ -48,6 +48,7 @@ This generator can also be further configured with the following command line fl
         --no-view        use static html instead of view engine
     -c, --css <engine>   add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)
         --git            add .gitignore
+        --typescript     add TypeScript support
     -f, --force          force on non-empty directory
     -h, --help           output usage information
 
diff --git a/bin/express-cli.js b/bin/express-cli.js
index d0c80b7f..b8dae952 100755
--- a/bin/express-cli.js
+++ b/bin/express-cli.js
@@ -57,6 +57,7 @@ program
   .option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css)')
   .option('    --git', 'add .gitignore')
   .option('-f, --force', 'force on non-empty directory')
+  .option('    --typescript', 'add TypeScript support')
   .parse(process.argv)
 
 if (!exit.exited) {
@@ -150,9 +151,30 @@ function createApplication (name, dir) {
     }
   }
 
+  if (program.typescript) {
+    pkg.scripts['build'] = 'tsc'
+
+    pkg.devDependencies = {}
+    pkg.devDependencies['typescript'] = '~3.7.5'
+    pkg.devDependencies['@types/node'] = '~13.7.0'
+    pkg.devDependencies['@types/debug'] = '~4.1.5'
+    pkg.devDependencies['@types/express'] = '~4.16.1'
+
+    pkg.dependencies['tslib'] = '~1.10.0'
+  }
+
   // JavaScript
-  var app = loadTemplate('js/app.js')
-  var www = loadTemplate('js/www')
+  var appTemplatePath
+  var wwwTemplatePath
+  if (program.typescript) {
+    appTemplatePath = 'ts/app.ts'
+    wwwTemplatePath = 'ts/www.ts'
+  } else {
+    appTemplatePath = 'js/app.js'
+    wwwTemplatePath = 'js/www'
+  }
+  var app = loadTemplate(appTemplatePath)
+  var www = loadTemplate(wwwTemplatePath)
 
   // App name
   www.locals.name = name
@@ -167,6 +189,9 @@ function createApplication (name, dir) {
   app.locals.modules.logger = 'morgan'
   app.locals.uses.push("logger('dev')")
   pkg.dependencies.morgan = '~1.9.1'
+  if (program.typescript) {
+    pkg.devDependencies['@types/morgan'] = '~1.7.37'
+  }
 
   // Body parsers
   app.locals.uses.push('express.json()')
@@ -176,6 +201,9 @@ function createApplication (name, dir) {
   app.locals.modules.cookieParser = 'cookie-parser'
   app.locals.uses.push('cookieParser()')
   pkg.dependencies['cookie-parser'] = '~1.4.4'
+  if (program.typescript) {
+    pkg.devDependencies['@types/cookie-parser'] = '~1.4.2'
+  }
 
   if (dir !== '.') {
     mkdir(dir, '.')
@@ -206,13 +234,25 @@ function createApplication (name, dir) {
   }
 
   // copy route templates
+  var routeScriptsDir
+  var routeScriptsNameGlob
+  if (program.typescript) {
+    routeScriptsDir = 'ts/routes'
+    routeScriptsNameGlob = '*.ts'
+  } else {
+    routeScriptsDir = 'js/routes'
+    routeScriptsNameGlob = '*.js'
+  }
   mkdir(dir, 'routes')
-  copyTemplateMulti('js/routes', dir + '/routes', '*.js')
+  copyTemplateMulti(routeScriptsDir, dir + '/routes', routeScriptsNameGlob)
 
   if (program.view) {
     // Copy view templates
     mkdir(dir, 'views')
     pkg.dependencies['http-errors'] = '~1.6.3'
+    if (program.typescript) {
+      pkg.devDependencies['@types/http-errors'] = '~1.6.3'
+    }
     switch (program.view) {
       case 'dust':
         copyTemplateMulti('views', dir + '/views', '*.dust')
@@ -250,21 +290,33 @@ function createApplication (name, dir) {
       app.locals.modules.compass = 'node-compass'
       app.locals.uses.push("compass({ mode: 'expanded' })")
       pkg.dependencies['node-compass'] = '0.2.3'
+      if (program.typescript) {
+        copyTemplate('declarations/node-compass.d.ts', path.join(dir, 'node-compass.d.ts'))
+      }
       break
     case 'less':
       app.locals.modules.lessMiddleware = 'less-middleware'
       app.locals.uses.push("lessMiddleware(path.join(__dirname, 'public'))")
       pkg.dependencies['less-middleware'] = '~2.2.1'
+      if (program.typescript) {
+        pkg.devDependencies['@types/less-middleware'] = '~2.0.31'
+      }
       break
     case 'sass':
       app.locals.modules.sassMiddleware = 'node-sass-middleware'
       app.locals.uses.push("sassMiddleware({\n  src: path.join(__dirname, 'public'),\n  dest: path.join(__dirname, 'public'),\n  indentedSyntax: true, // true = .sass and false = .scss\n  sourceMap: true\n})")
       pkg.dependencies['node-sass-middleware'] = '0.11.0'
+      if (program.typescript) {
+        pkg.devDependencies['@types/node-sass-middleware'] = '0.0.31'
+      }
       break
     case 'stylus':
       app.locals.modules.stylus = 'stylus'
       app.locals.uses.push("stylus.middleware(path.join(__dirname, 'public'))")
       pkg.dependencies['stylus'] = '0.54.5'
+      if (program.typescript) {
+        pkg.devDependencies['@types/stylus'] = '0.48.32'
+      }
       break
   }
 
@@ -325,15 +377,30 @@ function createApplication (name, dir) {
   if (program.git) {
     copyTemplate('js/gitignore', path.join(dir, '.gitignore'))
   }
+  if (program.typescript) {
+    copyTemplate('ts/tsconfig.json', path.join(dir, 'tsconfig.json'))
+  }
 
   // sort dependencies like npm(1)
   pkg.dependencies = sortedObject(pkg.dependencies)
+  if (pkg.devDependencies) {
+    pkg.devDependencies = sortedObject(pkg.devDependencies)
+  }
 
   // write files
-  write(path.join(dir, 'app.js'), app.render())
+  var appScriptName
+  var wwwScriptName
+  if (program.typescript) {
+    appScriptName = 'app.ts'
+    wwwScriptName = 'bin/www.ts'
+  } else {
+    appScriptName = 'app.js'
+    wwwScriptName = 'bin/www'
+  }
+  write(path.join(dir, appScriptName), app.render())
   write(path.join(dir, 'package.json'), JSON.stringify(pkg, null, 2) + '\n')
   mkdir(dir, 'bin')
-  write(path.join(dir, 'bin/www'), www.render(), MODE_0755)
+  write(path.join(dir, wwwScriptName), www.render(), MODE_0755)
 
   var prompt = launchedFromCmd() ? '>' : '$'
 
@@ -346,6 +413,11 @@ function createApplication (name, dir) {
   console.log()
   console.log('   install dependencies:')
   console.log('     %s npm install', prompt)
+  if (program.typescript) {
+    console.log()
+    console.log('   compile code:')
+    console.log('     %s npm run build', prompt)
+  }
   console.log()
   console.log('   run the app:')
 
diff --git a/templates/ts/app.ts.ejs b/templates/ts/app.ts.ejs
new file mode 100644
index 00000000..1c21171d
--- /dev/null
+++ b/templates/ts/app.ts.ejs
@@ -0,0 +1,51 @@
+<% if (view) { -%>
+import createError from 'http-errors';
+<% } -%>
+import express from 'express';
+import * as path from 'path';
+<% Object.keys(modules).sort().forEach(function (variable) { -%>
+import <%- variable %> from '<%- modules[variable] %>';
+<% }); -%>
+
+<% Object.keys(localModules).sort().forEach(function (variable) { -%>
+import <%- variable %> from '<%- localModules[variable] %>';
+<% }); -%>
+
+let app = express();
+
+<% if (view) { -%>
+// view engine setup
+<% if (view.render) { -%>
+app.engine('<%- view.engine %>', <%- view.render %>);
+<% } -%>
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', '<%- view.engine %>');
+
+<% } -%>
+<% uses.forEach(function (use) { -%>
+app.use(<%- use %>);
+<% }); -%>
+
+<% mounts.forEach(function (mount) { -%>
+app.use(<%= mount.path %>, <%- mount.code %>);
+<% }); -%>
+
+<% if (view) { -%>
+// catch 404 and forward to error handler
+app.use((req, res, next) => {
+  next(createError(404));
+});
+
+// error handler
+app.use(((err, req, res, next) => {
+  // 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');
+}) as express.ErrorRequestHandler);
+
+<% } -%>
+export default app;
diff --git a/templates/ts/declarations/node-compass.d.ts b/templates/ts/declarations/node-compass.d.ts
new file mode 100644
index 00000000..36ae2438
--- /dev/null
+++ b/templates/ts/declarations/node-compass.d.ts
@@ -0,0 +1,4 @@
+declare module 'node-compass' {
+    const compass: any;
+    export default compass;
+}
diff --git a/templates/ts/routes/index.ts b/templates/ts/routes/index.ts
new file mode 100644
index 00000000..de994eaf
--- /dev/null
+++ b/templates/ts/routes/index.ts
@@ -0,0 +1,9 @@
+import * as express from 'express';
+const router = express.Router();
+
+/* GET home page. */
+router.get('/', (req, res, next) => {
+  res.render('index', { title: 'Express' });
+});
+
+export default router;
diff --git a/templates/ts/routes/users.ts b/templates/ts/routes/users.ts
new file mode 100644
index 00000000..f4166100
--- /dev/null
+++ b/templates/ts/routes/users.ts
@@ -0,0 +1,9 @@
+import * as express from 'express';
+const router = express.Router();
+
+/* GET users listing. */
+router.get('/', (req, res, next) => {
+  res.send('respond with a resource');
+});
+
+export default router;
diff --git a/templates/ts/tsconfig.json b/templates/ts/tsconfig.json
new file mode 100644
index 00000000..f6a6ea84
--- /dev/null
+++ b/templates/ts/tsconfig.json
@@ -0,0 +1,66 @@
+{
+  "compilerOptions": {
+    /* Basic Options */
+    // "incremental": true,                   /* Enable incremental compilation */
+    "target": "es5",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
+    "module": "commonjs",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
+    // "lib": [],                             /* Specify library files to be included in the compilation. */
+    // "allowJs": true,                       /* Allow javascript files to be compiled. */
+    // "checkJs": true,                       /* Report errors in .js files. */
+    // "jsx": "preserve",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
+    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
+    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
+    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
+    // "outFile": "./",                       /* Concatenate and emit output to single file. */
+    // "outDir": "./",                        /* Redirect output structure to the directory. */
+    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+    // "composite": true,                     /* Enable project compilation */
+    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
+    // "removeComments": true,                /* Do not emit comments to output. */
+    // "noEmit": true,                        /* Do not emit outputs. */
+    "importHelpers": true,                    /* Import emit helpers from 'tslib'. */
+    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+
+    /* Strict Type-Checking Options */
+    "strict": true,                           /* Enable all strict type-checking options. */
+    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
+    // "strictNullChecks": true,              /* Enable strict null checks. */
+    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
+    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
+    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
+    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
+    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */
+
+    /* Additional Checks */
+    // "noUnusedLocals": true,                /* Report errors on unused locals. */
+    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
+    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
+    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
+
+    /* Module Resolution Options */
+    "moduleResolution": "node",               /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
+    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
+    // "typeRoots": [],                       /* List of folders to include type definitions from. */
+    // "types": [],                           /* Type declaration files to be included in compilation. */
+    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+    "esModuleInterop": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
+    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
+    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */
+
+    /* Source Map Options */
+    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
+    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
+    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+    /* Experimental Options */
+    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
+    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
+
+    /* Advanced Options */
+    "forceConsistentCasingInFileNames": true  /* Disallow inconsistently-cased references to the same file. */
+  }
+}
diff --git a/templates/ts/www.ts.ejs b/templates/ts/www.ts.ejs
new file mode 100644
index 00000000..97900578
--- /dev/null
+++ b/templates/ts/www.ts.ejs
@@ -0,0 +1,92 @@
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+
+import app from '../app';
+import debug from 'debug';
+import * as http from 'http';
+
+debug('<%- name %>:server');
+
+/**
+ * Get port from environment and store in Express.
+ */
+
+let port = normalizePort(process.env.PORT || '3000');
+app.set('port', port);
+
+/**
+ * Create HTTP server.
+ */
+
+let 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: string): string | number | boolean {
+  let 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: NodeJS.ErrnoException): void {
+  if (error.syscall !== 'listen') {
+    throw error;
+  }
+
+  let 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(): void {
+  let addr = server.address();
+  let bind = typeof addr === 'string'
+    ? 'pipe ' + addr
+    : 'port ' + addr!.port;
+  debug('Listening on ' + bind);
+}