在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类型时报错。
    "module": "esnext", // 模块引入方式
    "target": "esnext", // 指定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是变量名。

总结

本来还想再多写几个示例的,但是Dota2版本更新了,导致我不想继续写下去了,以后如果有时间再更新常用的示例吧。

本篇文章只专注于在React旧项目中安装并集成TypeScript,尽可能做到不涉及TypeScript的具体语法与介绍,因为介绍这些东西就不是一篇博客能搞定的了。

文章如有疏漏还请指正,希望能帮助到在TypeScript面前迟疑的你。

原文地址:https://www.cnblogs.com/vvjiang/p/11944912.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我最大的一个关于TypeScript的问题是,它将原型的所有方法(无论访问修饰符)编译.例classExample{publicgetString():string{return"HelloWorld";}privategetNumber():number{return123;}}众所周知,访问修饰符仅在编译时检
我对React很新,我正在尝试理解子组件之间相互通信的简洁方法.在一个简单的组件中,我知道我可以使用props将数据传递给子节点,并让子节点的回调将数据传递回父组件.在稍微复杂的情况下,当我在父组件中有多个子组件时,子组件之间的通信会有点混乱.我不确定我应该为同级别的儿童组
我有一个非常简单的表单,我将用户电子邮件存储在组件的状态,并使用onChange函数更新状态.有一个奇怪的事情发生在我的onChange函数用函数更新状态时,我在键入时在控制台中得到两个错误.但是,如果我使用对象更新状态,则不会出现错误.我相信用函数更新是推荐的方法,所以我很想知道为
我发现接口非常有用,但由于内存问题我需要开始优化我的应用程序,我意识到我并不真正了解它们在内部如何工作.说我有interfaceFoo{bar:number}我用这种类型实例化一些变量:letx:Foo={bar:2}Q1:这会创建一个新对象吗?现在,假设我想改变bar的值.我这样做有两种
我得到了一个json响应并将其存储在mongodb中,但是我不需要的字段也进入了数据库,无论如何要剥离不道德的字段?interfaceTest{name:string};consttemp:Test=JSON.parse('{"name":"someName","age":20}')asTest;console.log(temp);输出:{name:'someName
我试图使用loadsh从以下数组中获取唯一类别,[{"listingId":"p106a904a-b8c6-4d2d-a364-0d21e3505010","section":"section1","category":"VIPPASS","type":"paper","availableTi
我有以下测试用例:it("shouldpassthetest",asyncfunction(done){awaitasyncFunction();true.should.eq(true);done();});运行它断言:Error:Resolutionmethodisoverspecified.SpecifyacallbackorreturnaPromise;n
我正在一个有角度的2cli项目中工作,我必须创建一个插件的定义,因为它不存在它的类型.这个插件取决于已经自己输入的主库,它可以工作.无论如何,我有两个文件主要的一个图书馆类型文件AexportclassAextendsB{constructor(...);methodX():void;}我需要为我的
我有三元操作的问题:leta=undefined?"Defined!":"DefinitelyUndefined",b=abc?"Defined!":"DefinitelyUndefined",//ReferenceErrorc=(abc!==undefined)?"Defined!":"DefinitelyUndefin
下面的代码片段是30秒的代码网站.这是一个初学者的例子,令人尴尬地让我难过.为什么这样:constcurrentURL=()=>window.location.href;什么时候可以这样做?constcurrentURL=window.location.href;解决方法:第一个将currentURL设置为一个求值为window.location.href的
我是TypeScript和AngularJS的新手,我正在尝试从我的API转换日期,例如:"8/22/2015"…到ISO日期.将日期正确反序列化为Date类型的TypeScript属性.但是,当我尝试以下命令时(在typescript中,this.dateDisplay的类型为string)this.dateDisplay=formats.dateTimeValue.toISOString
我的名为myEmployees的数组中有5个名字,但是当我运行代码时,它只打印出其中的3个.我相信这种情况正在发生,因为脚本中的for循环覆盖了它在HTML文档中编写的前一行.我怎样才能解决这个问题?YearlyBulletinBoardAnnouncements!CongratulationstoTaylor,youhavebeenheref
我看到有一种方法可以在摩纳哥编辑器中设置scrolltop.如何滚动到特定行而不是特定像素?解决方法:如在文档中:https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.icodeeditor.html滚动到顶部,在px中:editor.setScrollPosition({scrollTop:0});滚动到特定
在从同一个类继承的一个数组中收集各种不同的对象时,如何在TypeScript中设置一个优等的类,以便TypeScript不显示错误?我正在尝试这样:interfaceIVehicle{modelName:string}interfaceICarextendsIVehicle{numberOfDoors:number,isDropTop:boolean}inte
什么是TypescriptTypeScript是一种由微软开发的自由和开源的编程语言,它是JavaScript的一个超集,扩展了JavaScript的语法。作者是安德斯大爷,Delphi、C#之父(你大爷永远是你大爷)。把弱类型语言改成了强类型语言,拥有了静态类型安全检查,IDE智能提示和追踪,代码重构简单、可读性
0.系列文章1.使用Typescript重构axios(一)——写在最前面2.使用Typescript重构axios(二)——项目起手,跑通流程3.使用Typescript重构axios(三)——实现基础功能:处理get请求url参数4.使用Typescript重构axios(四)——实现基础功能:处理post请求参数5.使用Typescript重构axios(五
1.1Typescript介绍1.TypeScript是由微软开发的一款开源的编程语言,像后端java、C#这样的面向对象语言可以让js开发大型企业项目。2.TypeScript是Javascript的超级,遵循最新的ES6、Es5规范(相当于包含了es6、es5的语法)。TypeScript扩展了JavaScript的语法。3.最新的Vu
0.系列文章1.使用Typescript重构axios(一)——写在最前面2.使用Typescript重构axios(二)——项目起手,跑通流程3.使用Typescript重构axios(三)——实现基础功能:处理get请求url参数4.使用Typescript重构axios(四)——实现基础功能:处理post请求参数5.使用Typescript重构axios(
webpack.config.jsconstpath=require('path');constCopyWebpackPlugin=require('copy-webpack-plugin');constExtractTextPlugin=require('extract-text-webpack-plugin');const{CleanWebpackPlugin}=require('clean-webpac
我在这篇ECMAScriptpage上读到“class”是JavaScript的一部分.在这个关于TypeScript的页面上,我看到’class’也可以在Typescript中找到.我的问题是,开发未来JavaScript应用程序的正确方法是利用(a)JavaScript中的面向对象功能以及EMACScript7.0中可用的功能或(b)使用TypeScript