Tom Dale 和其余人有一些关于 TypeScript 比较好的博文,跟随这些博文,我最近开始使用 TypeScript。今天,我将展现如何从零开始创建一个 TypeScript 工程,以及如何使用 Webpack 管理构建过程。我也将陈述关于 TypeScript 的第一印象,尤为是使用 TypeScript 和 ReactJS。javascript
我不会深刻到 TypeScript 语法的具体细节,你能够阅读 TypeScript handbook 或者免费书籍 TypeScript Deep Dive,它们是关于 TypeScript 比较好的入门材料。html
更新:若是你想用德语阅读这篇文章,你能够 thanks to the awesome folks at Reactx.de前端
第一步要作的事情是使用 Yarn 将 TypeScript 安装到本地的 node_modules
目录,首先,使用 yarn init
建立一个工程:java
yarn init yarn add typescript
当你安装了 TypeScript,你就可使用 tsc
命令行工具,这个工具能够编译 TypeScript,编译时会建立一个开始文件 tsconfig.json
,你能够编辑这个文件。你能够运行 tsc --init
得到这个文件 — 若是你已经在本地安装了 TypeScript
,你须要运行 ./node_modules/.bin/tsc --init
。node
注意:你能够在个人点开头的配置文件中看到,我将 $PATH
定义为 ./node_modules/.bin
这个目录。这有点危险,由于我可能不经意地运行这个目录下的任何可执行的文件,可是我愿意承担这个风险,由于我知道这个目录下安装了什么,并且这能节省不少打字时间!react
tsc --init
这个命令会生成一个 tsconfig.json
文件,全部 TypeScript
编译器的配置都存在于这个文件中。在默认配置的基础上,我作了一些修改,下面是我正在用的一个配置:webpack
{ "compilerOptions": { "module": "es6", // 使用 ES2015 模块 "target": "es6", // 编译成 ES2015 (Babel 将作剩下的事情) "allowSyntheticDefaultImports": true, // 看下面 "baseUrl": "src", // 能够相对这个目录 import 文件 "sourceMap": true, // 使 TypeScript 生成 sourcemaps "outDir": "ts-build", // 构建输出目录 (由于咱们大部分时间都在使用 Webpack,因此不太相关) "jsx": "preserve", // 开启 JSX 模式, 可是 "preserve" 告诉 TypeScript 不要转换它(咱们将使用 Babel) "strict": true, }, "exclude": [ "node_modules" // 这个目录下的代码不会被 typescript 处理 ] }
将这个属性的值设置为 true,它容许你使用 ES2015 默认的 imports 风格, 即便你导入的代码没有使用 ES2015 默认的 export。git
举个例子,当你 import 不是用 ES2015 编写的 React 时(虽然源码是,可是 React 使用一个构建好的版本),就能够利用上面的属性设置。这意味着,严格意义上来说,它没有使用 ES2015 默认的 export,因此当你使用 import 的时候, TypeScript 会警告你。尽管如此,像 Webpack 这样的构建工具可以导入正确的代码,因此我将这个属性设置为 true,相比使用 import * as React from 'react'
,我更喜欢 import React from 'react'
这种方式。es6
TypeScript 2.3 版本引入了一种新的配置选项,strict
。当将这个值设置为 true 时,TypeScript 编译器会尽量的严格 - 若是你将一些 JS 转为 TS,这可能不是你想要的,可是对于一些新的项目,使其尽量的严格是有意义的。它还引入了一些不一样的配置,其中几个比较重要的的有 noImplicitAny
和 strictNullChecks
:github
将 TypeScript 引入一个现有的项目,当你不声明变量的类型时,TypeScript 不会抛出错误。可是,当我从零开始新建一个 TypeScript 项目,我但愿编译器尽量地严格。
TypeScript 默认作的一件事是将变量设置为 any
类型。any
是 TypeScript 中避免类型检查的有效手段,它能够是任何值。当你转换 JavaScript 时,使用 any
是颇有用的,可是最好仍是尽量地严格。当将 noImplicitAny 设置为 true,你必须为变量设置类型。举个例子,当将 noImplicitAny
设置为 true 时,下面的代码会报错:
function log(thing) { console.log('thing', thing) }
若是你想了解更多关于 noImplicitAny 的信息,能够阅读 TypeScript Deep Dive
这是另外一个使 TypeScript 编译器更严格的选项。TypeScript Deep Dive 这本书有一个很好的章节介绍这个选项。若是将这个选项设置为true,TypeScript 会更容易识别出你引用的一个多是 undefined 值的地方,并将展现这个错误。例如:
person.age.increment()
当将 strictNullChecks 设置为 true,TypeScript 会认为 person 或者 person.age 多是 undefined,它会报个错以确保你处理它。这会防止出现运行时错误,因此这看起来是一个从一开始就要打开的很棒的选项。
我是 Webpack 的脑残粉;我喜欢它的插件生态系统、开发者工做流,喜欢它擅长管理复杂的应用和构建流程。因此,即便咱们可能仅仅使用 TypeScript 编译器,我仍然喜欢引入 Webpack。由于 TypeScript 输出 React 和 es6(也就是 es2015,babel 把 es6 转成 es5,因此咱们还须要 babel。让咱们安装 Webpack,Babel 和相关的 presets 及 ts-loader,ts-loader 是 TypeScript 在 Webpack 中的插件。
还有 awesome-typescript-loader ,也是 TypeScript 在 Webpack 中的插件,可是我首先找到的是 ts-loader
并且到目前为止它很是不错。若是谁使用了 awesome-typescript-loader
,我很乐意看到关于它们二者的对比。
yarn add webpack babel-core babel-loader babel-preset-es2015 babel-preset-react ts-loader webpack-dev-server
此时此刻,我必须感谢 Tom Duncalf,他在博客中发表的 TypeScript 1.9 + React,对我来讲,是一个特别好的开始,我极力推荐它。
在 Webpack 中没有特别的配置,可是我仍是在代码中列出一些注释来解释它:
const webpack = require('webpack') const path = require('path') module.exports = { // 设置 sourcemaps 为 eval 模式,将模块封装到 eval 包裹起来 devtool: 'eval', // 咱们应用的入口, 在 `src` 目录 (咱们添加到下面的 resolve.modules): entry: [ 'index.tsx' ], // 配置 devServer 的输出目录和 publicPath output: { filename: 'app.js', publicPath: 'dist', path: path.resolve('dist') }, // 配置 devServer devServer: { port: 3000, historyApiFallback: true, inline: true, }, // 告诉 Webpack 加载 TypeScript 文件 resolve: { // 首先寻找模块中的 .ts(x) 文件, 而后是 .js 文件 extensions: ['.ts', '.tsx', '.js'], // 在模块中添加 src, 当你导入文件时,能够将 src 做为相关路径 modules: ['src', 'node_modules'], }, module: { loaders: [ // .ts(x) 文件应该首先通过 Typescript loader 的处理, 而后是 babel 的处理 { test: /\.tsx?$/, loaders: ['babel-loader', 'ts-loader'], include: path.resolve('src') } ] }, }
咱们按照上面的方式配置 loaders ,从而使 .ts(x)
文件首先通过 ts-loader
的处理。按照 tsconfig.json
中的配置,使用 TypeScript 编译 .ts(x)
文件 - 输出 ES2015
。而后,咱们使用 Babel 将它降级到 ES5。为了实现这些,我建立了一个包含须要的 presets 的 .babelrc
文件:
{ "presets": ["es2015", "react"] }
如今咱们已经作好了写 TypeScript 应用的准备。
如今,咱们准备好创建 src/index.tsx
,这是咱们这个应用的入口。咱们能够建立一个虚拟的组件,渲染它,查看它是否正常运行。
import React from 'react' import ReactDOM from 'react-dom' const App = () => { return ( <div> <p>Hello world!</p> </div> ) } ReactDOM.render(<App />, document.getElementById('app'))
若是你运行 webpack,会看到下面的错误:
ERROR in ./src/index.tsx (1,19): error TS2307: Cannot find module 'react'. ERROR in ./src/index.tsx (2,22): error TS2307: Cannot find module 'react-dom'.
发生上面的错误是由于 TypeScript 试图确认 React 的类型、React 导出了什么。对于 React DOM,TypeScript 会作一样的事情。React 并非使用 TypeScript 编写的,因此它并无包含那些信息。幸运地是,为了应对这种状况,社区已经建立了 DefinitelyTyped,这是一个大型的组件类型库。
最近,安装机制改变了;全部的类型被发布到 npm @types scope 下。为了得到 React 和 ReactDOM 的类型,咱们运行下面的命令:
yarn add @types/react yarn add @types/react-dom
经过上面的处理,错误不见了。不管什么时候,你安装一个依赖时,都应该试着安装 @types
包,或者你想查看是否有被支持的类型,你能够在 TypeSearch 网站上查看。
为了在本地运行 app,咱们只须要运行 webpack-dev-server
命令。我配置了一个脚本 start
, 它能作上面的事情:
"scripts": { "start": "webpack-dev-server" }
服务会找到 webpack.config.json 这个文件,使用它建立咱们的应用。
若是你运行 yarn start
,你会看到来自于 webpack-dev-server 的输出,包含 ts-loader
的输出,这些可以确认应用是否正常运行。
$ webpack-dev-server Project is running at http://localhost:3000/ webpack output is served from /dist 404s will fallback to /index.html ts-loader: Using typescript@2.3.0 and /Users/jackfranklin/git/interactive-react-introduction/tsconfig.json Version: webpack 2.4.1 Time: 6077ms Asset Size Chunks Chunk Names app.js 1.14 MB 0 [emitted] [big] main webpack: Compiled successfully.
为了可以在本地看到效果,我建立了一个 index.html 文件,让它加载编译后的代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>My Typescript App</title> </head> <body> <div id="app"></div> <script src="dist/app.js"></script> </body>
在端口 3000,咱们将会看到 Hello world!
,咱们让 TypeScript
运行了!
在如今的工程中,我想使用 React Ace module 包含一个代码编辑器。可是这个模块并不提供 types,而且也没有 @types/react-ace
。在这种状况下,咱们必须在应用中增长类型,这样可使 TypeScript 知道如何去检查它的类型。这看起来很是烦人,让 TypeScript 至少知道全部第三方依赖关系的好处是,能够节省调试时间。
定义一个只包含类型的文件,后缀是 .d.ts
( ‘d‘ 表明 ‘declaration‘ ),你能够从 TypeScript docs 了解更多。在你的工程中,TypeScript 将会自动地找到这些文件,你不须要显式地导入它们。
我建立了 react-ace.d.ts
文件,添加下面的代码,建立模块,定义它的默认 export 为一个 React 组件。
declare module 'react-ace' { interface ReactAceProps { mode: string theme: string name: string editorProps?: {} showPrintMargin?: boolean minLines?: number maxLines?: number wrapEnabled?: boolean value: string highlightActiveLine?: boolean width?: string fontSize?: number } const ReactAce: React.ComponentClass<ReactAceProps> export = ReactAce }
我首先建立了一个 TypeScript 接口,这个接口包含组件的属性,export = ReactAce
标明组件经过模块被导出。经过定义属性的类型,TypeScript 会告诉我是否弄错了属性的类型或者忘记设置一个类型,这是很是有价值的。
最后,使用 TypeScript,我也想有一个很好的测试方案。我是 Facebook 的 Jest 的超级粉丝,我在 google 上作了一些搜索,确认它是否能用 TypeScript 运行。结果发现,这是可行的,ts-jest
包能够作一些很重的转换。除此以外,还有一个作类型检查的 @types/jest
包,可让你全部的测试都是类型确认过的。
很是感谢 RJ Zaworski
,他在博客上发表了 TypeScript and Jest ,这使我开始了解这个主题。若是你安装了 ts-jest
,你只须要在 package.json
中配置 Jest,下面是配置:
"jest": { "moduleFileExtensions": [ "ts", "tsx", "js" ], "transform": { "\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js" }, "testRegex": "/*.spec.(ts|tsx|js)$" },
第一个配置告诉 Jest 寻找 .ts
和 .tsx
文件。 transform
对象告诉 Jest 经过 ts-jest 预处理器运行任何 TypeScript 文件,ts-jest 预处理器经过 TypeScript 编译器运行 TypeScript 文件,产出能让 Jest 识别的 JavaScript。最后,我更新了 testRegex
设置,目的是寻找任何 *.spec.ts(x)
文件,我更喜欢用这种方式命名转换。
经过上面这些配置,我能够运行 jest
而且让每一件事情都如预期同样运行。
尽管 TypeScript 在代码中会给出不少检查提示,我仍然想要一个规范器,作些代码风格和质量检查。就像 JavaScript 的 ESLint,TSLint 是检查 TypeScript 的最好选择。它和 ESlint 的工做方式相同 - 用一系列生效或不生效的规则,还有一个 TSLint-React 包增长 React 的具体规则。
你能够经过 tslint.json
文件配置 TSLint,个人配置文件以下。我用 tslint:latest
和 tslint-react
presets,它们可使用不少规则。我不同意一些默认设置,因此我重写了它们 - 你能够和个人配置不一样 - 这彻底取决于你!
{ "defaultSeverity": "error", "extends": ["tslint:latest", "tslint-react"], "jsRules": {}, "rules": { // 用单引号, 可是在 JSX 中,强制使用双引号 "quotemark": [true, "single", "jsx-double"], // 我更喜欢没有分号 :) "semicolon": [true, "never"], // 这个规则使每一个接口以 I 开头,这点我不喜欢 "interface-name": [true, "never-prefix"], // 这个规则强制对象中的 key 按照字母顺序排列 "object-literal-sort-keys": false }, "rulesDirectory": [] }
我能够运行 tslint --project tsconfig.json
规范个人项目
总之,到目前为止,用 TypeScript 开发我很高兴。我确定会发表更多博文来描述这门语言的细节和我是如何使用 TypeScript 的。但仅就以下操做而言,构建过程、配置全部的工具、开始使用类型,这真是一种享受。若是你正在将你的 JS 应用结构化,想要一个更强大的编译器避免错误并减小调试时间,我极力推荐你尝试 TypeScript。
若是你想看源码或者以本文中的例子做为开始,我在 GitHub 上放了一个例子,若是你有任何问题,能够提 issue。
iKcamp原创新书《移动Web前端高效开发实战》已在亚马逊、京东、当当开售。