Tom Dale 和其他人有一些关于 TypeScript 比较好的博文,跟随这些博文,我最近开始使用 TypeScript。今天,我将展示如何从零开始建立一个 TypeScript 工程,以及如何使用 Webpack 管理构建过程。我也将陈述关于 TypeScript 的第一印象,尤其是使用 TypeScript 和 ReactJS。
 
我不会深入到 TypeScript 语法的具体细节,你可以阅读 TypeScript handbook 或者免费书籍 TypeScript Deep Dive,它们是关于 TypeScript 比较好的入门材料。
 
**更新:**如果你想用德语阅读这篇文章,你可以 thanks to the awesome folks at Reactx.de

安装配置 TypeScript

第一步要做的事情是使用 Yarn 将 TypeScript 安装到本地的node_modules目录,首先,使用yarn init创建一个工程:
yarn init
yarn add typescript  
 
当你安装了 TypeScript,你就可以使用 tsc 命令行工具,这个工具可以编译 TypeScript,编译时会创建一个开始文件 tsconfig.json,你可以编辑这个文件。你可以运行 tsc --init 获得这个文件 — 如果你已经在本地安装了 TypeScript,你需要运行 ./node_modules/.bin/tsc --init。
 
注意:你可以在我的点开头的配置文件中看到,我将 $PATH 定义为 ./node_modules/.bin 这个目录。这有点危险,因为我可能不经意地运行这个目录下的任何可执行的文件,但是我愿意承担这个风险,因为我知道这个目录下安装了什么,而且这能节省很多打字时间!
 
tsc --init 这个命令会生成一个 tsconfig.json 文件,所有 TypeScript 编译器的配置都存在于这个文件中。在默认配置的基础上,我做了一些修改,下面是我正在用的一个配置:

{
"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 处理
]
}  

allowSyntheticDefaultImports

将这个属性的值设置为 true,它允许你使用 ES2015 默认的 imports 风格, 即使你导入的代码没有使用 ES2015 默认的 export。
 
举个例子,当你 import 不是用 ES2015 编写的 React 时(虽然源码是,但是 React 使用一个构建好的版本),就可以利用上面的属性设置。这意味着,严格意义上来讲,它没有使用 ES2015 默认的 export,所以当你使用 import 的时候, TypeScript 会警告你。尽管如此,像 Webpack 这样的构建工具能够导入正确的代码,所以我将这个属性设置为 true,相比使用 import * as React from 'react',我更喜欢 import React from 'react' 这种方式。

strict:true

TypeScript 2.3 版本引入了一种新的配置选项,strict。当将这个值设置为 true 时,TypeScript 编译器会尽可能的严格 - 如果你将一些 JS 转为 TS,这可能不是你想要的,但是对于一些新的项目,使其尽可能的严格是有意义的。它还引入了一些不同的配置,其中几个比较重要的的有noImplicitAny和strictNullChecks:

noImplicitAny

将 TypeScript 引入一个现有的项目,当你不声明变量的类型时,TypeScript 不会抛出错误。但是,当我从零开始新建一个 TypeScript 项目,我希望编译器尽可能地严格。 TypeScript 默认做的一件事是将变量设置为any类型。any是 TypeScript 中避免类型检查的有效手段,它可以是任何值。当你转换 JavaScript 时,使用any是很有用的,但是最好还是尽可能地严格。当将 noImplicitAny 设置为 true,你必须为变量设置类型。举个例子,当将noImplicitAny设置为 true 时,下面的代码会报错:
function log(thing) {
console.log('thing', thing)
}  
如果你想了解更多关于 noImplicitAny 的信息,可以阅读TypeScript Deep Dive

strictNullChecks

这是另一个使 TypeScript 编译器更严格的选项。TypeScript Deep Dive 这本书有一个很好的章节介绍这个选项。如果将这个选项设置为true,TypeScript 会更容易识别出你引用的一个可能是 undefined 值的地方,并将展示这个错误。例如:
person.age.increment()  
当将 strictNullChecks 设置为 true,TypeScript 会认为 person 或者 person.age 可能是 undefined,它会报个错以确保你处理它。这会防止出现运行时错误,所以这看起来是一个从一开始就要打开的很棒的选项。

配置 Webpack, Babel and TypeScript

我是 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 应用的准备。

写一个 TypeScript React 组件

现在,我们准备好建立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

为了在本地运行 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>  

定义一个模块类型

在现在的工程中,我想使用 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 并且让每一件事情都如预期一样运行。

使用 TSLint 规范代码

尽管 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。
 
文章转自:Github
 

开始使用 TypeScript 和 React的更多相关文章

  1. 【react】使用 create-react-app 构建基于TypeScript的React前端架构----上

    写在前面 一直在探寻,那优雅的美:一直在探寻,那精湛的技巧:一直在探寻,那简单又直白,优雅而美丽的代码. ------ 但是在JavaScript的动态类型.有时尴尬的自动类型转换,以及 “0 == ...

  2. 使用typescript开发react应用

    初始化 mkdir project-dir cd project-dir yarn init -y 安装依赖 yarn add react react-dom yarn add -D typescri ...

  3. 三千字讲清TypeScript与React的实战技巧

    很多时候虽然我们了解了TypeScript相关的基础知识,但是这不足以保证我们在实际项目中可以灵活运用,比如现在绝大部分前端开发者的项目都是依赖于框架的,因此我们需要来讲一下React与TypeScr ...

  4. 使用TypeScript创建React Native

    ⒈初始化 React Native环境 参考https://reactnative.cn/docs/getting-started.html ⒉安装React Native官方的脚手架工具 npm i ...

  5. 从零搭建TypeScript与React开发环境

    前言 平时进行开发大多数是基于vue-cli或者create-react-app等官方或者公司内部搭建的脚手架.   我们业务仔做的最多就是npm i和npm run dev或者npm start,然 ...

  6. TypeScript with React

    TypeScript with React # Make a new directory $ mkdir react-typescript # Change to this directory wit ...

  7. TypeScript在React项目中的使用总结

    序言 本文会侧重于TypeScript(以下简称TS)在项目中与React的结合使用情况,而非TS的基本概念.关于TS的类型查看可以使用在线TS工具TypeScript游乐场 React元素相关 Re ...

  8. typescript实现react中的批次式更新

    欢迎吐槽讨论 前言 笔者在React经常使用setState,在学习过程中作笔记以作总结,欢迎讨论. 关于setState的核心观点 1 . 执行setState不都是异步的. 2 . setStat ...

  9. [闲的蛋疼系列]从零开始用TypeScript写React的UI组件(0)-先写一个Button??

    0.咸鱼要说的 一入前端深似海,咸鱼入海更加咸. 最近闲的蛋疼,手上年前的事也完成了7788了,借助[PG1]的话来说,我们要keep real. 咸鱼肯定不real 了,因为我们都活在梦里,所以咱们 ...

随机推荐

  1. Mybatis-02-CRUD及配置解析

    CRUD 1.namespace namespace中的包名要和Dao/Mapper接口的包名一致! 2.select 选择,查询语句; id:对应的namespace中的方法名 resultType ...

  2. CSS动画实例:跳跃的字符

    1.翻转的字符 在页面中放置一个类名为container的层作为容器,在该层中放置5个字符区域,HTML代码描述如下: <div class="container"> ...

  3. AWS 学习笔记之 VPC

    原文:https://ericfu.me/aws-notes-vpc/ VPC 把 VPC 想象成一个逻辑上的数据中心 包含一个 IGW (Internet Gateway)或者 Virtual Pr ...

  4. Trapdoors for Hard Lattices and New Cryptographic Constructions

    郑重声明:原文参见标题,如有侵权,请联系作者,将会撤销发布! 以下是对本文关键部分的摘抄翻译,详情请参见原文. Abstract 我们展示了如何构造各种“trapdoor”密码工具,假设标准格问题的最 ...

  5. Java14版本特性【一文了解】

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  6. day37:MySQL基本操作

    目录 part1:登录mysql的完整语法 part2:查询用户/设置密码/去除密码 part3:给ip/网段/所有ip设置账号密码 part4:查看权限 part5:添加权限/删除权限/删除用户 p ...

  7. 基于 GitBook 搭建个人博客

    目录 基于 GitBook 搭建个人博客 1.为什么要写博客? 2.为什么选择使用 GitBook 来搭建? 3.搭建方式 3.1 GitBook 线上直接搭建 3.2 由基于Node.js的命令工具 ...

  8. 从零开始的SpringBoot项目 ( 八 ) 实现基于Token的用户身份验证

    1.首先了解一下Token uid: 用户唯一身份标识 time: 当前时间的时间戳 sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接 固定参数(可 ...

  9. 力扣Leetcode 98. 验证二叉搜索树

    验证二叉搜索树 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和右子树自身 ...

  10. 源码浅入浅出 Java ConcurrentHashMap

    从源码的角度深入地分析了 ConcurrentHashMap 这个线程安全的 HashMap,希望能够给你一些帮助. 老读者就请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程 ...