在React旧项目中安装并使用TypeScript的实践
前言
本篇文章默认您大概了解什么是TypeScript,主要讲解如何在React旧项目中安装并使用TypeScript。
写这个的目的主要是网上关于TypeScript这块的讲解虽然很多,但都是一些语法概念或者简单例子,真正改造一个React旧项目使用TypeScript的文章很少。
所以在这里记录下改造一个React项目的实践。
博客内容部分参照 TypeScript中文网,这个网站有官方文档的中文版。
安装TypeScript及相关库
对于集成了TypeScript的脚手架可以略过这一步,这里主要讲一下如何将TypeScript集成到一个React脚手架中。
首先执行
npm install --save @types/react @types/react-dom
这一步主要是为了获取react和react-dom的声明文件,因为并不是所有的库都有TypeScript的声明文件,所以通过运行
npm install --save @types/库名字
的方式来获取TypeScript的声明文件。
只有获取了声明文件,才能实现对这个库的类型检查。
如果你使用了一些其它的没有声明文件的库,那么可能也需要这么做。
然后运行命令:
npm install --save-dev typescript awesome-typescript-loader source-map-loader
这一步,我们安装了typescript、awesome-typescript-loader和source-map-loader。
awesome-typescript-loader可以让Webpack使用TypeScript的标准配置文件tsconfig.json编译TypeScript代码。
source-map-loader使用TypeScript输出的sourcemap文件来告诉webpack何时生成自己的sourcemaps,源码映射,方便调试。
添加TypeScript配置文件
在项目根目录下创建一个tsconfig.json文件,以下为内容示例:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。这并不影响代码的输出,仅为了类型检查。
"outDir": "./dist/", // 重定向输出目录
"sourceMap": true, // 生成相应的 .map文件
"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错。(默认为false,个人建议也为false,可以兼容之前的js代码,这里改为true是为了我自己检测哪些类型需要处理)
"module": "esnext", // 模块引入方式
"target": "es6", // 指定ECMAScript目标版本
"moduleResolution": "node", // 决定如何处理模块
"lib": [
"esnext",
"dom"
], // 编译过程中需要引入的库文件的列表。
"skipLibCheck": true, //忽略所有库中的声明文件( *.d.ts)的类型检查。
"jsx": "react" // 在 .tsx文件里支持JSX
},
"include": [
"./src/**/*", // 这个表示处理根目录的src目录下所有的.ts和.tsx文件,并不是所有文件
]
}
skipLibCheck非常重要,并不是每个库都能通过typescript的检测。
moduleResolution设为node也很重要。如果不这么设置的话,找声明文件的时候typescript不会在node_modules这个文件夹中去找。
更多配置文件信息可以参考:这里。
配置webpack
这里列出一些TypeScript需要在webpack中使用的配置。
解析tsx文件的rule配置
示例如下:
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['react', 'env', 'stage-0', 'stage-3'],
plugins: [
'transform-decorators-legacy',
['import', { libraryName: 'antd', style: 'css' }], // `style: true` 会加载 less 文件
],
},
},
},
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" }
//...
]
//...
}
其实就只是多加了一行:
{ test: /\.tsx?$/, loader: "awesome-typescript-loader" }
注意这一行需要加在解析jsx的rule下面,因为rule的执行顺序是从下往上的,先解析tsx和ts再解析js和jsx。
当然用
enforce: 'pre'
调整过rule顺序的可以不用在意这一点。
解决使用css-moudule的问题
如果代码中使用了以下这种代码:
import styles from './index.css'
那么很可能报下面的错:
Cannot find module './index.css'
解决方法就是在根目录下新建文件一个叫declaration.d.ts的文件,内容为:
declare module '*.css' {
const content: any;
export default content;
}
这行代码就是为所有的css文件进行声明。
同时需要更改一下我们之前的tsconfig.json文件,将这个文件路径放在include中:
"include": [
"./src/**/*",
"./declaration.d.ts"
]
这个问题有通过安装一些库来解决的办法,但是会给每个css生成一个声明文件,感觉有点奇怪,我这里自己考虑了一下采用了上面这种方法。
用于省略后缀名的配置
如果你惯于在使用
import Chart from './Chart/index.jsx'
时省略后缀,即:
import Chart from './Chart/index'
那么在webpack的resolve中同样需要加入ts和tsx:
resolve: {
extensions: [".ts", ".tsx", ".js", ".jsx"]
},
引入Ant Design
实际上这个东西Ant Design的官网上就有怎么在TypeScript中使用:在 TypeScript 中使用。
那么为什么还是要列出来呢?
因为这里要指出,对于已经安装了Ant Design的旧项目而言(一般都是配了按需加载的吧),在安装配置TypeScript时上面这个文档基本没有任何用处。
在网上可以搜到的貌似都是文档中的方案,而实际上我们需要做的只是安装ts-import-plugin。
npm i ts-import-plugin --save-dev
然后结合之前的 awesome-typescript-loader ,在webpack中进行如下配置
const tsImportPluginFactory = require('ts-import-plugin')
module.exports = {
// ...
module: {
rules: [
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader",
options: {
getCustomTransformers: () => ({
before: [tsImportPluginFactory([
{
libraryName: 'antd',
libraryDirectory: 'lib',
style: 'css'
}
])]
}),
},
exclude: /node_modules/
}
]
},
// ...
}
配置完成,修改前的准备
注意,直到这一步,实际上您的项目在编译过程中仍然没有用到TypeScript。
因为我们这里只会用TypeScript处理.ts和.tsx后缀的文件,除非在配置中将allowJs设为true。
在使用之前,默认您已经对TypeScript语法有了了解,不了解可以参考:5分钟上手TypeScript。
也就是说,经过了上面的这些步骤,您的原有代码在不改动后缀的情况下应该是可以继续用的。
如果要使用TypeScript,那么新建tsx和ts文件,或者修改原有的文件后缀名即可。
接下来会列出一些典型的修改示例。
函数式组件的修改示例(含children)
import React from 'react'
import styles from './index.css'
interface ComputeItemProps {
label: string;
children: React.ReactNode;
}
function ComputeItem({ label, children }: ComputeItemProps) {
return <div className={styles['item']}>
<div className={styles['label']}>{label}:</div>
<div className={styles['content']}>{children}</div>
</div>
}
export default ComputeItem
这个例子中语法都可以在TypeScript的官网查到,唯一需要注意的是children的类型是React.ReactNode。
class组件修改示例(含函数声明,事件参数的定义)
import React from 'react'
import styles from './index.css'
interface DataSourceItem {
dayOfGrowth: string;
netValueDate: string;
}
interface ComputeProps {
fundCode: string;
dataSource: DataSourceItem[];
onChange(value: Object): void;
}
export default class Compute extends React.Component<ComputeProps, Object> {
// 改变基金代码
handleChangeFundCode = (e: React.ChangeEvent<HTMLInputElement>) => {
const fundCode = e.target.value
this.props.onChange({
fundCode
})
}
render() {
//...
);
}
}
这个例子展示如何声明class组件:
React.Component<ComputeProps, Object>
语法虽然看起来很怪,但是这就是TypeScript中的泛型,以前有过C#或者Java经验的应该很好理解。
其中,第一个参数定义Props的类型,第二个参数定义State的类型。
而react的事件参数类型应该如下定义:
React.ChangeEvent<HTMLInputElement>
这里同样使用了泛型,上面表示Input的Change事件类型。
而组件的Prop上有函数类型的定义,这里就不单独列出来了。
这几个例子算是比较典型的TypeScript与React结合的例子。
处理window上的变量
使用写在window上的全局变量会提示window上不存在这个属性。
为了处理这点,可以在declaration.d.ts这个文件中定义变量:
// 定义window变量
interface Window{
r:string[]
}
其中r是变量名。
处理别名问题(2019年12月2日更新)
如果你的代码在webpack中定义了别名,即:
resolve: {
alias: {
common: path.resolve(__dirname, 'src/common'),
modules: path.resolve(__dirname, 'src/modules'),
store: path.resolve(__dirname, 'src/store'),
}
},
那么使用这种别名的时候:
import { PageReduxState } from 'store/reducers';
typescript会找不到相应模块。
为了解决这个问题,需要在tsconfig.json中配置解析路径:
{
"compilerOptions": {
// ...
// ts下别名的检测
"baseUrl": "./src",
"paths": {
"common/*": ["common/*"],
"modules/*": ["modules/*"],
"store/*": ["store/*"]
}
},
//...
}
typescript解析扩展运算符报错(2019年12月13日更新)
报错提示如下:
ERROR in ./src/modules/pageMain/index.tsx 40:16
Module parse failed: Unexpected token (40:16)
You may need an appropriate loader to handle this file type.
| this.handleChange = (params) => {
| this.setState({
> ...this.state,
| ...params
| }, this.getList);
@ ./src/route/index.js 28:9-35
@ ./src/app.js
@ multi babel-polyfill ./src/app.js
解决方案:
尝试将tsconfig.json中
"target": "esnext"
修改为
"target": "es6"
即可。
总结
本来还想再多写几个示例的,但是Dota2版本更新了,导致我不想继续写下去了,以后如果有时间再更新常用的示例吧。
本篇文章只专注于在React旧项目中安装并集成TypeScript,尽可能做到不涉及TypeScript的具体语法与介绍,因为介绍这些东西就不是一篇博客能搞定的了。
文章如有疏漏还请指正,希望能帮助到在TypeScript面前迟疑的你。
在React旧项目中安装并使用TypeScript的实践的更多相关文章
- Immutable.js 以及在 react+redux 项目中的实践
来自一位美团大牛的分享,相信可以帮助到你. 原文链接:https://juejin.im/post/5948985ea0bb9f006bed7472?utm_source=tuicool&ut ...
- webpack学习(一)项目中安装webpack
如何在项目中安装webpack,webpack-cli? 前提:电脑安装了 node和npm包管理工具 1 创建项目文件夹或者在已有的项目中打开终端 输入相关命令: npm init 因为已经安装好 ...
- SuperDiamond在JAVA项目中的三种应用方法实践总结
SuperDiamond在JAVA项目中的三种应用方法实践总结 1.直接读取如下: @Test public static void test_simple(){ PropertiesConfigur ...
- ios项目中安装和使用CocoaPods
CocoaPods是什么? http://code4app.com/article/cocoapods-install-usage http://blog.csdn.net/jjmm2009/arti ...
- iOS项目中安装和使用 Cocoapods
1.首先我们要打开我们的终端: 2.在终端输入 这条命令 gem sources -l 2.1如果是和我是一样的显示,则镜像已添加,无需更改,如果不一样,则需要进行更改 这里输出的如果是 https ...
- 在vue项目中安装使用Mint-UI
一.Mint UI 是 由饿了么前端团队推出的 一个基于 Vue.js 的移动端组件库,具有以下特性: 使用文档: http://mint-ui.github.io/#!/zh-cn Mi ...
- CocoaPods -- ios项目中安装和使用CocoaPods
CocoaPods是什么? 当你开发iOS应用时,会经常使用到很多第三方开源类库,比如JSONKit,AFNetWorking等等.可能某个类库又用到其他类库,所以要使用它,必须得另外下载其他类库,而 ...
- vue踩坑记-在项目中安装依赖模块npm install报错
在维护别人的项目的时候,在项目文件夹中安装npm install模块的时候,报错如下: npm ERR! path D:\ShopApp\node_modules\fsevents\node_modu ...
- vue 项目中安装npm--save-dev 和 --save 命令
在vue项目中我们常用npm install 安装模块或插件 有两种命令把他们写入到 package.json 文件里面去 例如安装axios 安装到开发环境npm axios --save-dev ...
随机推荐
- javascript运行时中的堆栈和队列
下面是一个理论上的模型,js引擎着重实现和优化了描述的这几个语义 可视化描述 栈(stack) var a = 10; function bar(x) { var b = 5; fn(x + b); ...
- Unity系统消息广播
# 1.前言Unity自带消息系统,如SendMessage等,此方法利用的反射,且会反射游戏物体上的所有组件,对性能不友好.而且由于参数为方法名称,所以如果使用代码混淆,则会无法调用 方法,且难以追 ...
- linux sqlite安装
wget http://www.sqlite.org/sqlite-3.6.16.tar.gz tar -zxvf sqlite-3.6.16.tar.gz cd sqlite-3.6.16 ./c ...
- 设计模式C++描述----21.解释器(Iterpreter)模式
一. 解释器模式 定义:给定一个语言,定义它的文法的一种表示,并定一个解释器,这个解释器使用该表示来解释语言中的句子. 结构如下: 代码如下: //包含解释器之外的一些全局信息 class Conte ...
- Android自定义控件:自适应大小的文本控件
需求 自适应大小的文本: 效果图: 项目开发中,开发人员根据UI人员提供的一套尺寸,布局了一些带文本的页面, 往往会少考虑一些数据极限的问题,造成机型屏幕适配问题. 例如: 文本(或数值)长度可变,如 ...
- Centos6.5 忘记密码解决方法
问题 原因 : 太久没用centos了 忘记密码了 很尴尬 快照也没说明密码.... 1.重启 centos 在开机启动的时候快速按键盘上的“E”键 或者“ESC”键(如果做不到精准快速可以在启动 ...
- 洛谷P2858 【[USACO06FEB]奶牛零食Treats for the Cows】
我们可以记录头和尾再加一个卖了的零食数目,如果头超过尾就return 0. 如果遇到需要重复使用的数,(也就是不为零的d数组)就直接return d[tuo][wei]. 如果没有,就取卖头一个与最后 ...
- P4873 [USACO14DEC] Cow Jog_Gold 牛慢跑(乱搞?二分?)
(话说最近写的这类题不少啊...) 化简:给定数轴上一系列点,向正方向移动,点不能撞在一起,如果碰到一起就需要放到另外一行,求要多少行才能满足所有点不相撞的条件. (被标签误解,老是想到二分答案... ...
- C# - VS2019 通过DataGridView实现对Oracle数据表的增删改查
前言 通过VS2019建立WinFrm应用程序,搭建桌面程序后,通过封装数据库操作OracleHelper类和业务逻辑操作OracleSQL类,进而通过DataGridView实现对Oracle数据表 ...
- python语言线程标准库threading.local源码解读
本段源码可以学习的地方: 1. 考虑到效率问题,可以通过上下文的机制,在属性被访问的时候临时构建: 2. 可以重写一些魔术方法,比如 __new__ 方法,在调用 object.__new__(cls ...