开发者社区> 宜信技术学院> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

搭建node服务(三):使用TypeScript

简介: 本文介绍了如何在node服务中使用TypeScript
+关注继续查看

搭建node服务(三):使用TypeScript

JavaScript 是一门动态弱类型语言,对变量的类型非常宽容。JavaScript使用灵活,开发速度快,但是由于类型思维的缺失,一点小的修改都有可能导致意想不到的错误,使用TypeScript可以很好的解决这种问题。TypeScript是JavaScript的一个超集,扩展了 JavaScript 的语法,增加了静态类型、类、模块、接口和类型注解等功能,可以编译成纯JavaScript。本文将介绍如何在node服务中使用TypeScript。

一、 安装依赖

npm install typescript --save
npm install ts-node --save
npm install nodemon --save

或者

yarn add typescript
yarn add ts-node
yarn add nodemon

另外,还需要安装依赖模块的类型库:

npm install @types/koa --save
npm install @types/koa-router --save
…

或者

yarn add @types/koa
yarn add @types/koa-router
…

二、 tsconfig.json

当使用tsc命令进行编译时,如果未指定ts文件,编译器会从当前目录开始去查找tsconfig.json文件,并根据tsconfig.json的配置进行编译。

1. 指定文件

可以通过files属性来指定需要编译的文件,如下所示:

{
  "files": [
    "src/server.ts"
  ]
}

另外也可以通过使用"include"和"exclude"属性来指定,采用类似glob文件匹配模式,如下所示:

{
  "include": [
   "src/**/*"
  ],
  "exclude": [
   "node_modules",
   "**/*.spec.ts"
  ]
}

支持的通配符:

    • 匹配0或多个字符(不包括目录分隔符)
  1. ? 匹配一个任意字符(不包括目录分隔符)
  2. **/ 递归匹配任意子目录

2. 常用配置

compilerOptions 属性用于配置编译选项,与tsc命令的选项一致,常用的配置如下所示:

{
  "compilerOptions": {
    // 指定编译为ECMAScript的哪个版本。默认为"ES3"
    "target": "ES6",
    // 编译为哪种模块系统。如果target为"ES3"或者"ES5",默认为"CommonJS",否则默认为"ES6"
    "module": "CommonJS",
    // 模块解析策略,"Classic" 或者 "Node"。如果module为"AMD"、"System"或者"ES6",默认为"Classic",否则默认为"Node"
    "moduleResolution": "Node",
    // 是否支持使用import cjs from 'cjs'的方式引入commonjs包
    "esModuleInterop": true,
    // 编译过程中需要引入的库。target为"ES5"时,默认引入["DOM","ES5","ScriptHost"];target为"ES6"时,默认引入["DOM","ES6","DOM.Iterable","ScriptHost"]
    "lib": ["ES6"],
    // 编译生成的js文件所输出的根目录,默认输出到ts文件所在的目录
    "outDir": "dist",
    // 生成相应的.map文件
    "sourceMap": true
  },
  "include": [
   "src/**/*"
  ],
  "exclude": [
   "node_modules",
   "**/*.spec.ts"
  ]
}

1) target

target是编译目标,可以指定编译为ECMAScript的哪个版本,默认为"ES3"。ECMAScript的版本有:"ES3" 、"ES5"、 "ES6" 或者 "ES2015"、 "ES2016"、 "ES2017"、"ES2018"、"ES2019"、 "ES2020"、"ESNext"。

2) module

module指定编译为哪种模块系统,如果target为"ES3"或者"ES5",默认为"CommonJS",否则默认为"ES6"。可选用的模块系统有:"None"、 "CommonJS"、 "AMD",、"System"、 "UMD"、"ES6"或者"ES2015"、"ESNext"。

3) moduleResolution

moduleResolution指定模块解析策略,模块解析策略有:"Classic"、"Node",如果module为"AMD"、"System"或者"ES6",默认为"Classic",否则默认为"Node"。

示例1:

在/root/src/moduleA.ts中以import { b } from "./moduleB" 方式相对引用一个模块。
Classic解析策略,查找过程:

/root/src/moduleB.ts
/root/src/moduleB.d.ts

Node解析策略,查找过程:

/root/src/moduleB.ts
/root/src/moduleB.tsx
/root/src/moduleB.d.ts
/root/src/moduleB/package.json (如果指定了"types"属性)
/root/src/moduleB/index.ts
/root/src/moduleB/index.tsx
/root/src/moduleB/index.d.ts

示例2:

在/root/src/moduleA.ts中以import { b } from "moduleB" 方式非相对引用一个模块。
Classic解析策略,查找过程:

/root/src/moduleB.ts
/root/src/moduleB.d.ts
/root/moduleB.ts
/root/moduleB.d.ts
/moduleB.ts
/moduleB.d.ts

Node解析策略,查找过程:

/root/src/node_modules/moduleB.ts
/root/src/node_modules/moduleB.tsx
/root/src/node_modules/moduleB.d.ts
/root/src/node_modules/moduleB/package.json (如果指定了"types"属性)
/root/src/node_modules/moduleB/index.ts
/root/src/node_modules/moduleB/index.tsx
/root/src/node_modules/moduleB/index.d.ts

/root/node_modules/moduleB.ts
/root/node_modules/moduleB.tsx
/root/node_modules/moduleB.d.ts
/root/node_modules/moduleB/package.json (如果指定了"types"属性)
/root/node_modules/moduleB/index.ts
/root/node_modules/moduleB/index.tsx
/root/node_modules/moduleB/index.d.ts

/node_modules/moduleB.ts
/node_modules/moduleB.tsx
/node_modules/moduleB.d.ts
/node_modules/moduleB/package.json (如果指定了"types"属性)
/node_modules/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts

4) esModuleInterop

esModuleInterop为true时,表示支持使用import d from 'cjs'的方式引入commonjs包。当commonjs模块转化为esm时,会增加 __importStar 和 __importDefault 方法来处理转化问题。

示例:

cjs为commonjs模块,代码如下:

module.exports = { name: 'cjs' };

另外一个模块以esm方式引用了cjs模块,代码如下:

import cjsDefault from 'cjs';
import * as cjsStar from 'cjs';

console.log('cjsDefault =', cjsDefault);
console.log('cjsStar =', cjsStar);

输出结果为:

cjsDefault = { name: 'cjs' }
cjsStar = { name: 'cjs', default: { name: 'cjs' } }

编译后生成的代码如下:

var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });

const cjs_1 = __importDefault(require("cjs"));
const cjsStar = __importStar(require("cjs"));

console.log('cjsDefault =', cjs_1.default);
console.log('cjsStar =', cjsStar);

5) lib

lib指定编译过程中需要引入的库。target为"ES5"时,默认引入["DOM","ES5","ScriptHost"];target为"ES6"时,默认引入["DOM","ES6","DOM.Iterable","ScriptHost"]。由于本示例TypeScript是用于服务端的,不需要使用DOM和ScriptHost,所以lib设为["ES6"]。

6) outDir

输出目录,编译生成的js文件所输出的根目录,默认输出到ts文件所在的目录。

7) sourceMap

是否生成source map文件,通过使用source map 可以在错误信息中可以显示源码位置。
要想根据source map 显示错误信息源码位置,还需要在入口文件引入source-map-support 模块,如下:

import 'source-map-support/register';

三、 脚本命令

入口文件为src/server.ts,package.json中的scripts配置如下:

  • package.json
{
  "scripts": {
    "dev": "nodemon --watch src -e ts,tsx --exec ts-node src/server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  },
  …
}
  1. 执行 npm run dev 命令可以启动开发环境,当src下的文件被修改后会自动重新启动服务。
  2. 执行 npm run build 命令会进行编译,由于tsconfig.json中 outDir 指定输出目录为dist,编译后的js文件将出输出到dist目录。
  3. 执行 npm run start 命令可以启动应用,启动前需要执行 npm run build 进行编译。

四、 自定义类型

TypeScript 会自动从 node_modules/@types 目录获取模块的类型定义,引用的模块都需要安装对应类型库,如:

npm install @types/koa --save

安装后,会在node_modules/@types 目录下找到koa 文件夹,该文件夹下有koa相关的类型定义文件。当引用koa模块时会自动引入node_modules/ 和 node_modules/@types下的 koa 包。如果某个模块没有类型库或者对某个模块进行了扩展需要修改类型定义,这时需要引入自定义的类型。

示例:给koa增加bodyparser中间件

1. 设置typeRoots

  • tsconfig.json
{
  "compilerOptions": {
…
   // 类型声明文件所在目录
   "typeRoots": ["./node_modules/@types", "./src/types"],
},
"include": [
   "src/**/*"
  ],
  "exclude": [
   "node_modules",
   "**/*.spec.ts"
 ]
}

src/types是存放自定义类型的目录,本示例中src/types目录已被include包含,如果自定义的类型目录未被include包含还需要在include中添加该目录。

2. 编写类型定义文件

  • src/types/koa/index.d.ts
import * as Koa from "koa";

declare module "koa" {
    interface Request {
        body?: object;
        rawBody: string;
    }
}

这里给koa的request对象增加body和rawBody两个属性,分别用于存放请求体的json对象和原始字符串。

3. 编写 jsonBodyParser 插件

  • src/middleware/jsonBodyParser.ts
import Koa from "koa";

function getRawBody(ctx: Koa.Context): Promise<string> {
  return new Promise((resolve, reject) => {
      try {
        let postData: string = '';
        ctx.req.addListener('data', (data) => {
          postData += data;
        });
        ctx.req.on('end', () => {
          resolve(postData);
        });
      } catch (e) {
        console.error('获取body内容失败', e);
        reject(e);
      }
  })
}

export default function jsonBodyParser (): Koa.Middleware {
    return async(ctx: Koa.Context, next: Koa.Next) => {
      const rawBody: string = await getRawBody(ctx);
      const request: Koa.Request = ctx.request;
      request.rawBody = rawBody;
      if (rawBody) {
        try {
          request.body = JSON.parse(rawBody);
        } catch (e) {
          request.body = {};
        }
      }
      await next();   
    };
}

jsonBodyParser()会返回一个koa中间件,这个中间件将获取请求体的内容,将原始内容字符串赋值到ctx.request.rawBody,将请求体内容json对象赋值到ctx.request.body。由于src/types/koa/index.d.ts自定义类型已经扩展了Koa.Request的这两个属性,执行npm run build命令,使用 tsc 进行编译,可以编译成功。但是当执行 npm run dev 时,会提示编译错误,那是因为ts-node默认不会根据配置中的files、include 和 exclude 加载所有ts文件,而是从入口文件开始根据引用和依赖加载文件。最简单的解决办法就是在 ts-node 命令后增加 --files 参数,表示按配置的files、include 和 exclude加载ts文件,如下:

  • package.json
{
  "scripts": {
    "dev": " nodemon --watch src -e ts,tsx --exec ts-node --files src/server.ts",
  }
}

五、 说明

本文介绍了如何在node服务中使用TypeScript,具体的TypeScript语法规则网上有很多相关的资料,这里就不再介绍了。本文相关的代码已提交到GitHub以供参考,
项目地址:https://github.com/liulinsp/node-server-typescript-demo

作者:宜信技术学院 刘琳

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
typescript24-类型推论
typescript24-类型推论
13 0
typescript90-使用类型文件声明类型
typescript90-使用类型文件声明类型
6 0
2020你应该知道的TypeScript学习路线【数组类型】
2020你应该知道的TypeScript学习路线【数组类型】
35 0
【详细教程】教你如何使用Node + Express + Typescript开发一个应用(一)
Express是nodejs开发中普遍使用的一个框架,下面要谈的是如何结合Typescript去使用。
152 0
如何优雅地在 React 中使用TypeScript,看这一篇就够了!(3)
毕业已有3月有余,工作用的技术栈主要是React hooks + TypeScript。其实在单独使用 TypeScript 时没有太多的坑,不过和React结合之后就会复杂很多。本文就来聊一聊TypeScript与React一起使用时经常遇到的一些类型定义的问题。阅读本文前,希望你能有一定的React和TypeScript基础
78 0
如何优雅地在 React 中使用TypeScript,看这一篇就够了!(2)
毕业已有3月有余,工作用的技术栈主要是React hooks + TypeScript。其实在单独使用 TypeScript 时没有太多的坑,不过和React结合之后就会复杂很多。本文就来聊一聊TypeScript与React一起使用时经常遇到的一些类型定义的问题。阅读本文前,希望你能有一定的React和TypeScript基础
198 0
如何优雅地在 React 中使用TypeScript,看这一篇就够了!(1)
毕业已有3月有余,工作用的技术栈主要是React hooks + TypeScript。其实在单独使用 TypeScript 时没有太多的坑,不过和React结合之后就会复杂很多。本文就来聊一聊TypeScript与React一起使用时经常遇到的一些类型定义的问题。阅读本文前,希望你能有一定的React和TypeScript基础
116 0
使用CoffeeScript编写Node.js 模块
Node.js 基于JavaScript编写应用,JavaScript是我的主要开发语言。CoffeeScript是编译为JavaScript的编程语言。为什么我们要用CoffeeScript来编写一段可重用的代码——模块呢?CoffeeScript是一个非常高阶的语言,将JavaScript、Ruby和Python中我最爱的部分结合在了一起。在本教程中,我将展示如何使用CoffeeScript为Node.js创建一个可复用的开源模块。最近我在创建一个播放列表分析模块时get了这个新技能。重点在于如何将一个快速的开发变成一个结构良好的Node.js模块。
58 0
TypeScript在node项目中的实践
TypeScript在node项目中的实践 TypeScript可以理解为是JavaScript的一个超集,也就是说涵盖了所有JavaScript的功能,并在之上有着自己独特的语法。最近的一个新项目开始了TS的踩坑之旅,现分享一些可以借鉴的套路给大家。
1162 0
+关注
宜信技术学院
宜信技术学院是宜信旗下的金融科技能力展示与输出平台。通过分享在金融科技领域的开源成果、研发实践促进金融科技生态圈企业创新升级。
129
文章
0
问答
来源圈子
更多
+ 订阅
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载