【51CTO.com快译】不可否认,TypeScript凭着智能感知、静态分析(又名“类型检查”)、以及内联文档等功能,已经在JavaScript社区中占据了一席之地。这些功能虽然并非TypeScript独有,但是能够在如下方面提高开发团队的生产力,并改进的代码质量:
通过实时的、自动完成的代码建议,实现更快的代码编写。
发现并提示代码中的拼写错误。
方便新的成员熟悉代码库。
方便不同编程能力的团队成员更好地协作。
可以防止破损的代码被自动部署。
方便更加便捷、安全地修改和维护旧的代码。
可用于项目的自动文档化。
下面,我将分不同的阶段,向您深入浅出地介绍如何开启TypeScript之旅。
阶段 1:在JavaScript文件中启用TypeScript
作为一种最为普及的代码编辑器,Visual Studio Code被广泛地用来编写JavaScript。不过,VS Code也内置了TypeScript,能够提供上面提到的智能感知和自动建议等基本功能。例如,我们可以创建一个带有属性hello的对象world。当我们试着去访问该对象的属性时,VS Code会自动推荐hello。不仅如此,它还会告诉我们该属性是一个字符串(string)类型。
这是一个非常基本但挺实用的类型检查。而且,就算代码库中存在少许错误,这样的类型检查也能够识别出来。例如,如果我们不小心将数字传递给了需要字符串类型的函数,那么就会被及时发现。因此,为了启用针对JavaScript文件的全面类型检查,您只需将注释// @ts-check添加到待检查的JavaScript文件的顶部便可。
据此,针对前面的例子,如果我们尝试着用数字类型覆盖对象的hello属性,那么我们将会收到一条“Type ‘number’ is not assignable to type ‘string'”的警告。我们之前的函数之所以不会给出任何错误提示,是因为TypeScript并不知道输入只能是字符串类型。为此,我们可以使用JSDoc向JavaScript添加各种类型。
此处的JSDoc是一个通过使用注释,将上下文文档添加到源代码中的系统。它可以被用于自动生成文档站点。TypeScript支持解析JSDoc的各项注释。对于前面的示例函数,我们可以告诉TypeScript,yell函数的第一个参数是str(字符串)类型,及该函数是一个“字符串”。
现在,当我们向函数传递一个数字时,就会看到一个红色的波浪形警告,而在将鼠标悬停其上方时,会出现“Argument of type ‘number’ is not assignable to parameter of type ‘string’.”的具体警告内容。您可以通过jsdoc.app,学习如何使用JSDoc记录各项内容。
阶段 2:在JavaScript项目中启用TypeScript
如果您处理的是大型JavaScript项目,那么逐一添加// @ts-check到每个文件中,显然过于繁琐。幸运的是,VS Code提供了一些方法,来自动化此类工作。其中的一种方法是将“Check JS”配置设置为true。也就是说,我们在settings.json文件中设置为"javascript.implicitProjectConfig.checkJs": true。
如果您想在项目级别上为团队中彼此协作的每个人都启用此功能,则可以通过将tsconfig.json文件添加到项目的根目录来实现。有关tsconfig.json中各种配置选项的详细信息,请参阅--https://www.staging-typescript.org/tsconfig。
JSON { "compilerOptions": { "checkJs": true, /* Report errors in .js files. */ } }
JSDocs支持许多内置的类型,其中包括:string、number、boolean、array、promise、function等。不过,您可能需要创建某些超出基本定义的类型。例如,在定义一个“Dog”对象类型时,它需要具有“品种(breed)”、“年龄(age)”、以及可选的“名字(name)”属性。那么,我们可以通过JSDoc来进行如下类型的定义:
Plain Text /** * @typedef {object} Dog * @property {string} breed * @property {number} age * @property {string} [name] */
除了这种通过语法的方式来定义对象以外,您还可以通过参阅JSDoc文档,获悉更多TypeScript的泛型和实用类型。
下面,我们来看如何使用types.js文件,来定义各种全局类型,并向代码库中导入类型定义。据此,我们将Dog的类型定义放入该文件中,通过引用相对路径,实现在各种不同文件中导入并使用该类型:
JSON /** @type {import('./types).Dog} */ const myDog = { breed: 'Chiweenie', age: 4, name: 'Nugget' }
如果需要让Dog能够在同一个文件中的多处使用到该类型,我们则可以在本地重新定义类型,以减少输入:
JSON /** @typedef {import('./types).Dog} Dog */ /** @type {Dog} */ const myDog = { breed: 'Chiweenie', age: 4, name: 'Nugget' }
您可能会发现,就目前而言,由于该文件不属于JavaScript模块,我们无法从types.js文件中导入任何内容。编辑器会提示:“File ‘/path/to/types.js’ is not a module.”。对此,您可以使用CommonJS或ES模块语法,导出该文件。其导出值并不重要,它甚至可以undefined。例如,它可以是下面的任何一行:
Plain Text // Works module.exports = {} // Sure exports.merp = '' // Why not? export default = null // Go for it export const thingamabob = undefined
当然,我们也可以是从第三方库导入的类型定义。其语法虽然非常相似,但是并不会使用相对路径,而是按照名称去引用库。例如,Vue.js(https://vuejs.org/)组件可以被输入为:
Plain Text /** @type {import('vue').Component} */
当然,并非所有库都会提供类型定义。如果您的库不提供类型定义,那么您可以去absolutetyped.org社区进行检索。VS Code有一个名为“自动类型获取(Automatic Type Acquisition)”的功能,会自动为你查找和安装来自社区的类型定义。
如果您愿意,也可以遵从上述语法,在TypeScript文件中编写类型定义,不过其文件扩展名为.ts。例如,如果想用TypeScript定义上述全局类型,我们可以将文件名更改为“type.ts”,其内容如下:
TypeScript export interface Dog { breed: string age: number name?: string }
阶段 3:将TypeScript集成到 CI/CD 管道中
下面,让我们讨论一些更为复杂的问题。如果在代码中引入了错误,我们可以阻止代码的部署吗?在开始讨论之前,我们假设:
您可以自如地使用命令行。
您对NPM已有一定的经验;如果没有,可以通过链接--https://docs.npmjs.com/getting-started,了解NPM的相关基础知识。
您已熟悉CI/CD(持续集成/持续交付)的概念。
您已经拥有一个使用package.json文件初始化了的NPM项目。
我们的目标是在CI/CD环境中运行TypeScript编译器,以便系统判定代码是否存在类型错误。为此,我们需要为CI/CD环境提供一个TypeScript版本,以及一个待运行的脚本。
首先在终端里,我们需要在该项目的同一个文件夹中运行如下命令:
npm install --save-devTypeScript
它会在本地安装TypeScript,并把Typecript包作为开发依赖项,包含在package.json文件中予以更新。在了解了有哪些依赖项已被安装后,TypeScript可以在不依赖VS Code的情况下服务于该项目。接着,我们可以使用如下命令,更新package.json文件的NPM脚本部分:
Plain Text "ts": "tsc"
上述命令会添加一个名为ts的新脚本,并运行“tsc”命令(即typescript编译器)。在运行“npm run ts”命令之前,我们需要解决两个问题:
1. TypeScript需要知道待运行文件的路径。
2. TypeScript只适用于.ts文件,而我们只有.js文件。
对此,您需要决定是继续编写JavaScript文件呢,还是去写TypeScript文件?就我而言,我认为将所有内容保留在JavaScript中会更加简单。毕竟TypeScript编译器能够很好地支持JavaScript文件,只不过未能在默认情况下启用罢了。
为了明确地告知TypeScript去检查哪些文件,我们需要使用allowJs配置,来允许它在JavaScript文件上运行。假设我们的JavaScript是写在./src/index.js文件中的,那么我们将有如下选择:
我们可以将“--allowJs ./src/index.js”添加到package.json文件中的NPM脚本中。
我们可以在每次调用NPM脚本:“npm run ts -- --allowJs ./src/index.js”时,添加上述命令。
我们可以在项目的根目录中使用tsconfig.json文件。
由于我们已经拥有一个tsconfig.json文件,因此可以直接使用它。同时,我们需要定义files数组,并将allowJs和noEmit设置为true:
JSON { "files": ["./src/index.js"], "compilerOptions": { "checkJs": true, /* Report errors in .js files. */ "allowJs": true, /* Allow parsing javascript. */ "noEmit": true, /* Do not emit outputs. */ } }
由于TypeScript通常被用于转译代码,因此我们可以在此将noEmit配置设置为true。这就意味着,它可以接受各种代码,并以某种方式对其进行转换。例如,它能够接收一个TypeScript文件,然后返回一个JavaScript文件。
运行“npm run ts”命令,我们不会看到任何配置错误,而只是一些与代码相关的错误。例如,在前面的示例中,如果我们试图覆盖被定义为字符串类型的属性,就会产生错误。
至此,我们已经准备好了将这种类型检查,集成到自动化部署的过程中。我们需要确保部署过程能够顺利地调用“npm run ts”命令。
值得一提的是,TypeScript虽然是一个很好的测试套件的补充,但它绝不是自动化测试的替代品。纵然TypeScript可以消除进入代码库时的各种类型错误,但是如果您的项目依赖于自动化部署的话,您还应该做好单元或集成测试。
TypeScript虽然可能会阻止您在应当运用数字的地方使用字符串,但是不会阻止您在只允许使用正数的情况下使用负数。因此,我建议您在系统中同时实施静态分析和自动化测试。而我最喜欢的JavaScript项目测试工具是Jest。
阶段 4:为开源库生成类型定义
definitelytyped.org之类的社区项目可以为TypeScript提供类型定义等支持。我们可以采用当前的设置,而无需其他繁琐的设置,让TypeScript为我们的项目创建类型定义文件。在完成之后,我们可以发布自己的库,以便用户拥有丰富的类型定义,进而协助改善用户与库交互的体验。
首先,我们需要对tsconfig.json文件进行更多的修改。其中包括:删除“noEmit”设置(或将其设置为false),将“declaration”和“emitDeclarationOnly”设置为true,并为“outDir”提供路径。下面展示了新的文件内容:
{ "files": ["./src/index.js"], "compilerOptions": { "checkJs": true, /* Report errors in .js files. */ "allowJs": true, /* Allow parsing javascript. */ "declaration": true, /* Generates '.d.ts' file. */ "emitDeclarationOnly": true, /* Only generate '.d.ts'. No JS */ "outDir": "./dist", /* Send output to this directory. */ } }
您可以为“outDir”任选一个路径,以便为生成的类型定义文件提供存放之处。由于我们已经使用了JavaScript,因此无需额外编译步骤,便可将“emitDeclarationOnly”设置为true。在构建步骤中,您也可以使用Babel.js和Rollup.js。
在生成了类型定义文件,并发送至/dist文件夹后,我们需要修改package.json文件,以便告知NPM各种文件的存在性,并让任何使用库的开发人员受益。为了在NPM处发布内容,我们不但需要注意“name”和“version”属性,还可以通过定义“types”(又名“typings”)属性,来告知TypeScript在哪个文件夹中,查找库的类型定义文件。当然,如果您的类型定义文件(以.d.ts结尾)与代码同处一个文件夹中,则无需上述设置。下面展示了package.json文件的示范性内容:
{ "name": "nuggetisthebest", "version": "1.0.0", "types": "dist", "scripts": { "ts": "tsc" }, "devDependencies": { "typescript": "^4.1.3" } }
请参见NPM文档,以获悉更多有关如何将库发布到NPM处。
禁止在某些代码上运行TypeScript
有时您在CI/CD管道中使用到了TypeScript,但并不希望它报告、甚至阻止项目的部署。对此,我们可以通过如下选项,来予以规避:
在某一行中禁用TypeScript:通过在任意行上添加注释// @ts-ignore,您可以禁用TypeScript对于该行的分析。
在整个文件上禁用TypeScript:如果想禁用TypeScript对整个文件的检查,您可以在文件的顶部添加注释// @ts-nocheck。
在文件组或目录组上禁用TypeScript:tsconfig.json文件有一个配置选项exclude,它允许您定义需要完全忽略的文件和目录。
复杂类型
最终,您可能会需要重载函数(overloaded functions)之类非常复杂的类型定义。您可以在JSDoc中使用各种TypeScript功能,轻松地从TypeScript文件(.ts扩展名)中导入相关的类型。
TypeScript /** @type { import('.types.ts').SomeType } */ const someType = {}
这意味着在任何复杂的情况下,我们都可以使用常规的TypeScript文件,并将自定义类型导入到JSDoc中,而无需为整个项目编写TypeScript。我们甚至都不需要依赖TypeScript编译器。此外,我们还可以使用.d.ts文件,来声明全局类型,具体请参见--https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html。
小结
总的说来,虽然使用.ts文件比较常见,但是我会基于如下原因,去使用JSDocs方法:
无需额外的构建步骤,只需普通的JavaScript。
可以将代码复制并粘贴到任何JavaScript项目中。
并未增添新的语法,因此容易上手。
将更少的“噪音”引入代码。
由于无需等待编译器,因此开发的速度会更快。
下面是更多有关JSDoc的资源,可供您深入研究:
由VS Code提供的《Working with JavaScript》
由Devhints提供的《JSDoc Cheatsheet》
由Robert Biggs提供的《Type-Safe JavaScript with JSDoc》
由Robert Biggs提供的《JavaScript Type Linting》
由Stefan Baumgartner提供的《TypeScript without TypeScript – JSDoc superpowers》
原文标题:Get Started With TypeScript the Easy Way,作者:Austin Gil