• 挑战Typescript项目中的strict编译模式


    概述

    将手上的angular项目升级最新版本,并将typescript也升级到最新版本,结果vs code中打开tsconfig.json有个红色的警告,说是添加strict选项,以减少类型错误。作为一个有强迫症的程序员,决定一试,用这个选项来提高代码的优雅度。

    加入strict选项后,运行ng build之后,原本运行正常的程序,突然之间出现了一堆错误。

    后边对修正的内容进行一一记录。

    变量参数

    用了strict参数后,所有参数变量都要指定类型。否则报这样的错误:

    error TS7006: Parameter 'name' implicitly has an 'any' type.

     本来any是没问题的,但现在必须显示指定。比如一个类方法是这样的:

    getDataStore(name, keyExpr, needpage?, aditional?) 

    现在就变成,最后一个参数可以直接指定any类型:

    getDataStore(name:string, keyExpr:string, needpage?:boolean, aditional?:any)  

     再如下边这个错误:

    error TS7005: Variable 'items' implicitly has an 'any[]' type.

    const items=[];

    ......

    return items;

     编译器认为这样隐匿any[]类型不行,要显式指出,改成这样就过了:

    const items:any[] = [];

    ……

    return items;

     当然有条件的话,能定义相应的接口类型,这样可以更加有利于定位错误。

    空值(null)检查

    用strict编译选项后,如果上下文不能保证变量不为null,则会报错,比如:

    error TS2531: Object is possibly 'null'.

    24     const modes = localStorage.getItem('modes').split(',');

     这里从本地存储中获取一个值。虽然业务上保证这地方不可能为null,但从编译器的角度来讲,只要不是直接定义赋值的地方都有可能为null,所以报了这个错。实际上就是要求split()方法前的对象必须有效。

    那这里修改思路有两个,一是给定默认值,另一个如果为null则不调用split(),这里采用后者,代码自改成如下:

    1. const str = localStorage.getItem('modes');
    2. if (str) {
    3. const modes = str.split(',');
    4. }

    编译通过。

    再比如以下错误:

    error TS2322: Type 'null' is not assignable to type '{ email: string; userName: string; }'.

    394     this._user = null;

     原因是_user在定义的时候,显式给字类型,而null并不是从给定类型继承的,所以不能赋值,这跟java,c#之类的编程语言不同,不能直接给null,此时还是用any大法,在定义的时候指定any类型:

    private _user: any = { email: '', userName: '' };

     还有属性为空的情况:

    error TS2531: Object is possibly 'null'.

    16     let p=route.routeConfig.path??'';

     改成这样:

     let p=route.routeConfig?.path??'';

     再来一个null问题:

    Error: src/app/shared/components/side-navigation-menu/side-navigation-menu.component.ts:85:7 - error TS2531: Object is possibly 'null'.

    85       this.menu.instance.collapseAll();

     代码修改为:

    this.menu?.instance?.collapseAll();

    编译通过。

     类型匹配

    当一个值可能为null的时候,会引起类型匹配的异常。比如下边这个错误,它是将null当成一种类型,因为userId有可能为null,所以出现了因类型不匹配而不能赋值的错误。考虑到此处null没有什么实际意义,可以强制为string类型。

    Argument of type 'string | null' is not assignable to parameter of type 'string | Blob'.
      Type 'null' is not assignable to type 'string | Blob'.

    353       formData.append('userId', this.userId);

    代码修改如下:

    formData.append('userId', this.userId + '');

     编译通过。

    看一个参数类型不一致的情况:

    error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.

    43     this.viewBox.push(width);

     这里把width改any类型即可:

    let width:any=....;

    属性初始化

    类属性如果没做初始化,也会编译报错:

    error TS2564: Property 'user' has no initializer and is not definitely assigned in the constructor. 

    21   user: { email: string, userName: string };

    这里给个初始值:

    1. @Input()
    2. user: { email: string; userName: string } = { email: '', userName: '' };

     感觉这地方有点多余,本来可以进行空值判断的,这么一改变成默认值判断了。

    不能初始化null的错误:

    Error: src/app/shared/components/side-navigation-menu/side-navigation-menu.component.ts:39:3 - error TS2322: Type 'null' is not assignable to type 'DxTreeViewComponent'.      

    39   menu: DxTreeViewComponent = null;

     因为null是个独立的类型,如果初始化需要事先设为null,那么要给变量指定null类型:

      @ViewChild('mapdiv', { static: true }) mapdiv: ElementRef | any= {};
    

    再看一个 never类型的错误:

    error TS2339: Property 'name' does not exist on type 'never'.

    72     return t?.name;

    原来代码是这样的:

    1. const t = this.usingVehicleTypes.find((el) => el.id === id);
    2. return t?.name;

     大概是find()返回值不符合Array所定义的返回类型,因此造成类型不匹配的错误,修改如下:

    1. const t:any = this.usingVehicleTypes.find((el:any) => el.id === id);
    2. return t?.name;

     暂时先去除类型检查,编译通过。

    总结

    这个项目开始时,是当成js来写了,充分使用了js的动态特性,所以大部分地方都没标注变量类型,在strict选项下,给变量加类型标注成了最主要的任务。由于json对象,基本从服务器端获得的,客户端并未定义相应的数据类型,有时加类型标注成了困难,好在有个any对象,暂时全部标注成any。总之,强类型的编程语言有利于代码的维护,但ts建立js之上的,有时不知不觉会去使用js的特性,而ts的强类型特性就会带来困扰,所以在用ts的时候,我们要注意这当中的变化。

  • 相关阅读:
    Go语言之集合类型
    聊聊并发编程——多线程之volatile
    Spring安全配置: 构建安全稳固的Java应用
    [位运算]leetcode2401:最长优雅子数组(medium)
    GetKeyState获取键盘状态(原神水龙王转转转)
    typora+github+picgo搭建图床401
    动态规划求股票买入、卖出最大收益 java 实现( 最多可进行 1 次 “买入 ==> 卖出“ 操作 )
    spring boot + minio 8.5.4 遇到 okhttp3包冲突
    【C++】new和delete深度解析
    linux elf relationship between data structures involved in symbol resolution
  • 原文地址:https://blog.csdn.net/icoolno1/article/details/111219027