React项目重构到TS+React版本

原文链接: https://juejin.im/post/5badbf5ce51d450e8477ce58

储备知识

基本数据类型

布尔 数字

元祖

const author:[string, string] = ['mx', '大风']
复制代码

枚举, 比如应用于订单状态

enum Order {
  finish = '订单完成',
  deleted = '订单删除',
  send = '订单已发出'
}
复制代码

any 放弃类型检查

  • ? 可有可无
function users(name?:string) {
  console.log(name)
}
function users(age:number = 18, name?:string) {
  console.log(name)
}
function users(...numbers: number[]) {
  console.log(name)
}
复制代码

class

public private protected


    public name: string;  //类里面 子类 其它任何地方外边都可以访问
    protected age: number; //类里面 子类 都可以访问,其它任何地方不能访问
    private money: number; //类里面可以访问, 子类和其它任何地方都不可以访问
复制代码

声明文件

安装 TypeScript 时,会顺带安装 lib.d.ts 声明文件。此文件包含了 JavaScript 运行时以及 DOM 中存在各种常见的环境声明

对于项目中一些全局可用的变量或者接口,可以考虑创建一个globals.d.ts

globals.d.ts 可以看做是 lib.d.ts 的扩充

接口

TypeScript的核心原则之一是对值所具有的结构进行类型检查

基本使用

在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

定义属性

interface props {
  readonly name: string
  habit? : string
}
复制代码

函数 && 接口

定义一个函数所需的形状

  • 函数声明
interface fnBox {
  (len:number, cont: number): number[]    
}
let pushAry:fnBox
pushAry = function (len: number, cont: number) {
  return new Array(len).fill(cont)    
}
复制代码

如果这里故意写错cont的类型,会有什么提示

可以定义无返回值的函数

interface props {
  once(): void 
  twice: () => void 
}
复制代码
  • 函数表达式

我们也可以使用函数表达式去书写函数

let sing = () => {-- do something --}
复制代码

函数表达式添加约束可以写为

let sing = (singer: string): boolean => {-- do something --}
复制代码

但是这种只是对等号右侧的匿名函数进行了类型定义,而等号左边的 sing,是通过赋值操作进行类型推论而推断出来的

手动给 sing 添加类型,则应该是这样:

let sing: (singer: string) => boolean = (singer: string): boolean => { -- do something-- }
复制代码

类 && 接口

implements 可以使类 具有接口的属性功能

interface int2 {
  once(): void,
  touchMore: (count: number) => void
}

class Btn implements int2 {
  once () {
    console.log('once')
  }
  touchMore (count: number) {
    console.log('touchMore')
  }
}

const btns = new Btn
btns.once()
btns.touchMore(9)
复制代码

class

配置 tsconfig.json

1 使用webpack搭建项目的时候,有配置alias参数

'utils': path.resolve(__dirname, '../utils')
复制代码

这样在组件中引入的时候,文件路径可以简化

import { log } from 'utils/statistics'
复制代码

不过在使用ts之后,会发现组件中有警告信息,表示找不到此模块

就需要另外在tsconfig中添加配置参数

    "baseUrl": "./",
    "paths": {
      "components": ["./components/*"],
      "utils": ["./utils/*"],
    }
复制代码

2 other

noEmitOnError: true
复制代码

当编译出错,则不输出编译后的文件

全局变量 window

在访问页面的时候,server会反馈一些基本的用户信息,比如用户名,设备版本号等,将这些全部挂载在CONFIG变量中了


const {uid, version} = window.CONFIG // 然后在组件中可以直接获取
复制代码

但是在加入TS之后,会提示window中不存在属性CONFIG

TS不允许获取,设置没有声明过的属性,所以这里报错了

处理方案

1 全局扩展

在模块内部添加声明到全局作用域

在入口文件index.tsx中 添加声明

 declare global {
   interface Window { CONFIG: any }
 }
 const {uid} = window.CONFIG
复制代码

2 使用类型断言

const { apk } = (window as any).CONFIG
复制代码

3 添加 globals.d.ts 声明文件

interface Window {
  CONFIG: any
}
复制代码

泛型

import * as React from 'react'

class App extends React.Component<props, state>{
    -- do something --
}
复制代码

定义

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性

比如写一个根据指定内容填充到指定长度的数组生成函数 getAry

function getAry (len: number, cont: string | number): string[] | number[] {
  return new Array(len).fill(cont)
}
复制代码

参数

参数 含义 类型
len 指定长度 number
cont 指定内容 string 或 number (使用联合类型处理)

由于不确定参数cont的输入类型,所以返回值使用了联合类型处理

这种需要 返回值类型和输入参数类型保持一致的情况,可以使用泛型T来处理

泛型是类型变量,一种用来表示类型的特殊变量

  • 使用方式

在函数名后添加<>

function createFn
function createFn // 明确指定 T 是 string 类型的
复制代码

其实泛型函数的类型与非泛型函数的类型没什么不同,只是多了一个类型参数在最前面

  • 可以指定默认类型

TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用

function beConfusion<T = boolean> (name: string, isDone: boolean) {...}
复制代码

泛型接口

将上面那个函数使用接口来定义

interface dealFn {
  (len: number, cont: T): T[]    
}
let getAry: dealFn
getAry = function  (len: number, cont: T): T[] {
 --do something--    
}
复制代码

泛型类

泛型类使用<>括起泛型类型,跟在类名后面

class DealAry<T> {
  value: T
  constructor (value: T) {
    this.value = value
  }
  deal () {
    console.log(this.value)
  }
}
复制代码

other

  • 其余的一些泛型变量

虽然可以自己定义泛型变量结构,但是一般会使用已定义好的泛型

Promise

async taobaoGoods =  function (ids: number[]): Promise<string> {
  return new Promise ((reslove, reject) => {
    try{
      reslove('success')
    } catch {
      reject('failture')
    } 
  }) 
}
复制代码
  • 可以有多个泛型变量
class 
复制代码

React.component 类的泛型变量

打开 node_modules/@types/react 可以看到 component 类

简单概括就是

class Component<P = {} , S = {} > {
  -- other --
  readonly props: Readonly<{ children?: ReactNode }> & Readonly

state: Readonly -- other -- } 复制代码

可以看到 props 和 state 两个对象都是只读的

所以我们在写React组件的时候,要调整为

interface BannerProps {
  showUpdate(): void,
  clickOnce: (type: string, id: number) => void
}

interface BannerState {
  once: boolean
}

export default class Banner extends React.Component<BannerProps, BannerState> {
  --do something--
} 

复制代码

类型断言

获取一个元素的某个属性

componentDidMount () {
  const target = this.refs.img
  if (target.getAttribute('src') != newSrc) { -- do something-- }   
}
复制代码

ts提示有错误

这是因为TS推断target 还不具备getAttribute 属性

TypeScript 允许你覆盖它的推断,并且能以你任何你想要的方式分析它,这种机制,被称为「类型断言」

TypeScript 类型断言用来告诉编译器你比它更了解这个类型,并且它不应该再发出错误

<类型>值 或者 值 as 类型
复制代码

*** 在jsx中必须使用 第二种方式去处理 ***

在不确定类型的时候就访问其中一个类型的属性或方法,可以使用类型断言

function getType (arg: string | boolean): boolean {
  if ( arg.length ) return true
  return false
}
复制代码

这种其实会报错的,如果输入是布尔值,则没有length

加入类型断言

function getType (arg: string | boolean): boolean {
  if ( (arg as string).length  ) return true
  //  if ( (arg).length ) return true
  return false
}
复制代码

断言成一个联合类型中不存在的类型是不允许的

 if ( (arg as array).length  ) return true // 报错
复制代码

所以上面组件里 需要调整为

const target = this.refs.img as HTMLElement 
复制代码

内置对象

JavaScript 中有很多内置对象,可以直接在 TypeScript 中当做定义好了的类型

const isEnd:Boolean = new Boolean(false)

interface createEle {
  createElement(tagName: "div"): HTMLDivElement    
}

复制代码

设置组件属性

interface videoProps {
    poster?: string
}

class Video extends Component<videoProps> {
    static defaultProps = {
        poster: ''
    }
}
复制代码

webpack 调整为 ts 格式

新项目中将webpack部分调整为ts处理

参考文章,官网地址

储备知识

ts-node 可以直接运行.ts文件 ts版本使用方式见官网

tsconfig

tsconfig.webpack.json中的配置直接按照官网中去写的

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "esModuleInterop": true
  }
}
复制代码

这里配置的是es5 将其调整为es6即可

或者使用 webpack-merge 进行模块合并

  
const config: Configuration = merge(commonPlugin, {...})

复制代码

未解决的问题

  • 关于 import 模块

项目中保留了一份 webpack.common.js的文件,发现在 import 模块的时候 会先找到 这个.js的 而不是 .ts

import commonPlugin from './webpack.common'
复制代码

ts.config 中有一个 allowJs 参数 如果设置为TRUE 则可以引入js文件 但是这个默认的是FALSE 所以应该不会有引入 js文件

所以很奇怪 - 文档

暂时将js文件名修改了

掉坑记录

::写法

this.update} />
复制代码

这个主要是借助了bable 的 transform-function-bind 双冒号实现绑定

TS不支持这种写法 可以调整为

 this.update} />
<Channel clickOnce={this.clickOnce.bind(this)} />
复制代码

issue

es6的新语法

组件中有使用

const result = Object.assign({}, params, info)
复制代码

处理方案

1 借助lodash

安装lodash.assign和@types/lodash

2 更换复制对象方案

3 调整tsconfig配置文件

之前

target: es5
复制代码

调整为

target: es6
复制代码

注意可能需要重启 VScode 才可以生效

3 img 元素属性

在获取img元素的src属性的时候,是这么写的

(target as HTMLElement).getAttribute('src')
复制代码

然后在直接设置src值的时候

(item as HTMLElement).src = itemSrc
复制代码

这样就会报错,需要调整为

(item as HTMLImageElement).src = itemSrc
复制代码

getAttribute 属于 HTMLElement 属性,而 src 属于 HTMLImageElement 的属性

补充一下 element HTMLElement node 的属性方法

element对象

htmlelement对象

node对象

后续还在调整中,继续更新掉坑记录部分

你可能感兴趣的:(React项目重构到TS+React版本)