视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
关于react项目静态类型检查方案
2020-11-27 19:34:02 责编:小采
文档
 这篇文章主要介绍了关于react项目静态类型检查方案,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下

为什么需要引入类型检查

JS作为一个弱类型语言,具有很大的灵活性,但是它的优点也是它的缺点,它很容易让我们忽视一些隐晦的逻辑,语法错误或数据类型错误,在编译期甚至运行时看上去都不会报错,但是可能会发生各种各样奇怪的和难以解决的bug。

function getPrice(x) {
 return x * 10;
}
getPrice('a23') // NaN
function getDefaultValue (key, emphasis) {
 let ret;
 if (key === 'name') {
 ret = 'GuangWong';
 } else if(key=== 'gender') {
 ret = 'Man';
 }else if(key ==='age'){
 ret = 18;
 } else {
 throw new Error('Unkown key ');
 }
 if (emphasis) {
 ret = ret.toUpperCase();
 }
 return ret;
 }
 
 getDefaultValue('name'); // GuangWong
 getDefaultValue('gender', true) // MAN
 getDefaultValue('age', true)

这是一个简单的函数,第一个参数 key 用来获得一个默认值。第二参数 emphasis 为了某些场景下要大写强调,只需要传入 true 即可自动将结果转成大写。

但是如果不小心将 age 的值写成了数字字面量,如果我调用 getDefaultValue('age', true) 就会在运行时报错。这个有可能是业务上线了之后才发生,直接导致业务不可用

除此以外,在工作中,我们也经常遇到过对象上的一个属性在n个模块之间传递之后变成了 undefined。以上是代码健壮性的问题,工作中比较头疼的另外一个问题是协作性问题:如何让一个别人提供的方法产出一个一目了然的文档?因为一个项目总会涉及到多人协作:同学 A 编写了函数 a(),而 同学 B 在调用函数 a() 的时候得一直撸着 API 文档才能知道 a() 需要什么参数,会返回什么参数。

而 同学 A 后续又改动了函数 a(),但是却忘记了更新文档,这时候新接手项目的 同学 C 看着 API 文档和函数 a() 一脸懵逼,问题浮出水面:团队协作中,提供的接口如何描述自身?

这其中涉及到的问题有:

1.接口如何描述自己的参数和返回值?
2.接口参数和返回值在无数需求迭代中改变了多次,而这个 API 对应的文档该如3.何更新?
4.数据格式如何描述?

为了解决上述诸多痛点,我们需要引入类型检查机制,所谓类型检查,就是在编译期尽早发现(由类型错误引起的)bug,又不影响代码运行(不需要运行时动态检查类型),使编写js具有和编写Java等强类型语言相近的体验,它可以:

  • 使得大型项目可维护

  • 提高效率,错误在编写代码时报错,而非编译阶段

  • 增强代码的可读性,可以做到代码即文档

  • 增强设计

  • 采用Flow

    出品的JavaScript静态类型检查工具,它可以部分引入,不需要完全重构整个项目,所以对于一个已有一定规模的项目来说,迁移成本更小,也更加可行

    flow使用学习成本也相对比较低

    1. 全局安装flow命令行工具

    npm install -g flow-bin
    1. 在项目根目录,创建.flowconfig文件

    2. 安装babel插件

    npm install --save-dev babel-plugin-transform-flow-strip-types
    1. 在.babelrc文件中添加插件

    {
     "presets": [ "es2015", "react", "stage-1" ],
     "plugins": [
     "react-flow-props-to-prop-types"
     ]
    }
    1. 安装扩展(??X):Flow Language Support

    2. 修改VS Code对JavaScript的默认配置

    Code -> 首选项 -> 用户设置(?,)
    搜索:javascript.validate.enable
    修改为:"javascript.validate.enable": false

    1. 在项目中使用

    在需要静态检查的文件头引入flow,如:

    /* @flow */
    function getPrice(x: number) {
     return x * 10;
    }
    getPrice('a23') // vscode 工具提示错误

    采用typescript

    TypeScript 被称是 JavaScript 的超集,是微软公司推出的一种静态代码检查的方案,在 JavaScript 上做了一层封装,封装出 TypeScript 的特性,当然最终代码可以编译为 JavaScript

    1.静态类型

    let num: number;
    num = 'likely';
     
    [ts] 不能将类型“"likely"”分配给类型“number”。
    let num: number

    2.函数表达
    js写法

    export const fetch = function (url, params, user) {
     // dosomething
    
     return http(options).then(data => {
     return data
     }).catch(err => {
     return err
     })
    }

    以上下是一个 JavaScript 的函数,不看方法内的写法我们完全不知道这个 API 会有哪些坑。

    export const fetch = function (url: string | object, params?: any, user?: User): Promise<object | Error> {
     // dosomething
    
     return http(options).then(data => {
     return data
     }).catch(err => {
     return err
     })
    }

    上述 TypeScript 包含了很多信息,让我们很方便地知道该函数怎么调用

  • url 可能是 string 或 object 类型

  • params 是可以不传的,也可以传递任何类型

  • user 要求是 User 类型的,当然也是可以不传

  • 返回了一个 Promise,Promise 的求值结果可能是 object,也有可能是 Error

  • 3.组件

    export interface CouponProps { 
     coupons: CouponItemModel[]; 
    }
    
    export interface couponState {
     coupons: CouponItemModel[],
     page: number,
     size: number,
     state: number, //可用优惠券
     hasMore: boolean,
     isLoading: boolean,
     loadedError: boolean,
    }
    
    
    class CouponContainer extends React.Component<CouponProps, couponState> {
    }

    上述组件我们可以清晰地知道一个组件有哪些属性,哪些方法,哪些属性是必传的,哪些是可选的,一目了然,真正做到了代码即文档

    关于typescript还有很多其他特点,如类,接口,泛型等,具体可参考官方文档
    https://www.typescriptlang.org/

    项目迁移typescript

    1.node
    (1)使用npm安装:npm install -g typescript,当前项目使用了是v2.8.3
    (2)2.2 tsconfig.json

    {
     "compilerOptions": {
     "module": "commonjs",
     "target": "es5",
     "noImplicitAny": true,
     "sourceMap": true,
     "lib": ["es6", "dom"],
     "outDir": "dist",
     "baseUrl": ".",
     "jsx": "react",
     "paths": {
     "*": [
     "node_modules/*",
     "src/types/*"
     ]
     }
     },
     "include": [
     "src/**/*"
     ]
    }

    (3)将.js文件改为.ts
    (4)结合 gulp 进行实时编译

    var gulp = require('gulp');
    var pump = require('pump');
    var webpack = require('webpack');
    
    var ts = require('gulp-typescript');
    var livereload = require('gulp-livereload');
    var tsProject = ts.createProject("tsconfig.json");
    
    gulp.task('compile:tsc:server', function () {
     return gulp.src('src/server/**/*.ts')
     .pipe(tsProject())
     .pipe(gulp.dest('dist/server'));
    });
    //将任务同步执行
    var gulpSequence = require('gulp-sequence');
    gulp.task('compile', gulpSequence(
     'compile:tsc:server',
    ))
    
    
    gulp.task('watch', ['compile'], function() {
     livereload.listen();
    
     gulp.watch(['./src/server/**/*.ts'], ['compile:tsc:server']);
    })
    1. react

    可在 webpack 配置文件添加规则

    { 
     test: /\.tsx?$/, 
     enforce: 'pre',
     use: [
     {
     loader: "ts-loader"
     }
     ]
     },

    3.遇到的问题
    遇到的问题

  • 动态地为global添加属性

  • 由于js灵活的风格,我们经常动态地为某一对象添加属性,但是typeScript是编译型语言,基本原则是先定义再使用,所以当我们像下面这么引用

    global.testName = '哈哈';

    便会出现这样的错误

    类型“Global”上不存在属性“testName”

    解决方法

    (1)将global强制转化为any类型
    
     (<any>global).testName = '哈哈'
     
    (2)扩展原有的对象
    
     global.prototy.testName = '哈哈哈'
    
    (3)使用.d.ts文件
    declare namespace NodeJS {
     
     export interface Global {
     testName: string;
     }
    }

    网上很多方法是直接添加一个.d.ts文件即可,但是亲测无效,需要在引用文件引入该文件,如本项目在app.ts文件中引入了

    /// <reference path="../types/custom.d.ts" />

    Flow 与 TypeScript简单对比

    总结

    Flow或者TypeScript都是静态类型检查的优秀解决方案,能够给有类型检查需求的一定规模的项目带来实际收益。基于现有项目的情况,迁移 TypeScript 时间成本比较大,学习曲线相对陡峭,建议现有项目采用 Flow 方案,对于一些新的项目,可以采用 TypeScript

    下载本文
    显示全文
    专题