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前端高效开发实战》已在亚马逊、京东、当当开售。