最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • TypeScript进阶, 如何避免 any

    正文概述 掘金(夜尽灬天明丶)   2021-05-14   859

    为什么会出现 any

    • 不知道如何准确的定义出类型,TS 报错了,用 any 能解决,便用 any
    • 觉得定义类型浪费时间,项目经理催的紧,工期紧张,any 更方便

    频繁使用 any 的弊端

    • 不利于良好的编码习惯
    • 不利于项目的后续维护
    • 会出现很多本可避免的 bug

    非必要不使用 any 的好处

    • 良好的代码提示
    • 强大的静态类型检查
    • 可读性和可维护性

    所以,我们要对 AnyScript 说不!

    TS 容易出现 any 的场景梳理

    给 window 全局对象增加属性

    常常能见到这样的写法

    ;(<any>window).obj = {}(
      // 或
      window as any
    ).obj = {}
    

    这样做,在使用时和赋值时都需要断言一次,非常麻烦,并且使用时也不能得到代码提示

    正确的做法应该是

    1. 在项目全局的 xxx.d.ts 文件中配置如下代码
    interface Window {
      obj: {}
    }
    
    1. 在需要给 window 赋值的文件目录下级新建一个 @types 文件夹,并在其中新建 index.d.ts 文件,添加如下代码
    interface Window {
      obj: {}
    }
    

    方法 2 也会在全局的 window 上增加 obj 这一声明,如果新增属性使用的跨度比较大,则推荐放在项目的 index.d.ts 中更利于维护,两种方式都在全局给 window 添加了属性,但方法 1 能一眼看出项目中 window 中添加了什么属性

    正确使用可选链、非空断言

    错误的理解 typescript 的可选参数,而使用断言导致隐患

    const a: {
      b: string
      c?: {
        d: string
      }
    } = {
      b: "123",
    }
    
    console.log((<any>a).c.d) // 错误,这样访问会报错,应使用可选链
    console.log(a.c!.d) // 错误,ts 不会将错误抛出,但实际访问也会报错
    

    ! 非空断言与 as 有相似之处,主要用于断言某个属性的值不为 nullundefined,它不会影响最终的代码产物,只会影响代码编译过程中的类型校验

    ?. 可选链操作符 会影响编译后的代码产物,如:

    这段 ts 代码

    const a = {
      c: undefined,
    }
    
    const b = a?.c?.d
    

    会被编译为如下 js 代码( babel 在线编译网站 )

    "use strict"
    
    var _a$c
    
    const a = {
      c: undefined,
    }
    const b =
      a === null || a === void 0
        ? void 0
        : (_a$c = a.c) === null || _a$c === void 0
        ? void 0
        : _a$c.d
    

    将对象属性类型关联起来

    对象中有多个属性是联合类型,其中 a 属性和 b 属性是有关联的,a1 时,bstringa2 时,bnumber 我们通常是这样定义的

    const obj: {
      a: 1 | 2
      b: string | number
    } = {
      a: 1,
      b: "1.2"
    }
    

    那么使用时,会造成需要用断言来再次限定 b 的范围的情况,如下代码段所示

    if (obj.a === 1) {
      const [left, right] = (obj.b as string).split(".")
    }
    // 如果你偷懒,那可能又变成了这样的情况
    if (obj.a === 1) {
      const [left, right] = (obj.b as any).split(".")
    }
    

    有没有什么办法能让我们不再 as 一次呢?有

    const obj: {
      a: 1
      b: string
    } | {
      a: 2
      b: number
    } = {
      a: 1,
      b: "1.2"
    }
    // 你会发现这样定义了以后,不需要再次进行断言限定 obj.b 的范围
    if (obj.a === 1) {
      const [left, right] = obj.b.split(".") // 校验通过
    }
    

    如果我们把这样的方法应用到函数(也可以用重载实现)传参或组件传参,有意思的是它还能限定传参的范围, 函数组件实现:

    TypeScript进阶, 如何避免 any

    错误的传参,ab 的类型不匹配,校验不通过

    TypeScript进阶, 如何避免 any

    正确的传参,校验能通过

    TypeScript进阶, 如何避免 any

    注意:你不能将 props 解构出来,会导致两者的关系丢失

    const { a, b } = props // 错误,a 和 b 的类型关系丢失
    

    是否使用联合类型需要辩证的看待,在任何时候都用上述方法定义可能会造成一些臃肿

    巧用类型保护避免断言

    typescript 中,常用的类型保护为 typeofinstanceof、和 in 关键字 掌握上述关键字较为容易,可通过文档了解 还有一个关键字 is (类型谓词)是 typescript 提供的,是另一种“类型保护”(这种说法助于理解)

    类型谓词能让我们通过函数的形式做出复杂的类型检验的逻辑,一个使用类型谓词的函数的声明往往是如下形式:

    type X = xxxx // 某种类型
    function check(params): params is X
    

    理解起来就是如果 check 函数返回了真值,则参数 paramsX 类型,否则不一定是 X 类型

    设想一下如下场景,某个项目,既可能运行在微信网页中,也可能运行在其他 webview

    在微信网页中,微信客户端向 window 对象中注入了各种 native 方法,使用它的方式就是 window.wx.xxxx()

    在其他 webview 中,我们假设也有这样的 native 方法,并且使用它的方式为 window.webviewnative.xxxx()

    在 typescript 项目中,window 对象上并不会默认存在 wxwebviewnative 两个属性,参考 给 window 全局对象增加属性,我们能显示地为 wxwebviewnative 两个属性定义类型:

    interface Window {
        wx?: {
            xxxx: Function
        }
        webviewnative?: {
            xxxx: Function
        }
    }
    

    如果你不会这样做,那可能又会写成断言为 any(window as any).wx.xxxx()

    可以看到在上面的代码段中两个属性都被我定义为了可选属性,目的是为了在后续维护(迭代)中,防止不做判断直接链式调用

    在微信环境中 window.wx 一定存在,但 webviewnative 一定不存在,反之在其他的 webview 中,(见前文假设)window.webviewnative 一定存在

    在接口 interface 中,我们并不能动态的知晓和定义到底哪个存在

    你可以这样写

    if (typeof window.wx !== 'undefined') {
        window.wx.xxxx()
    } else {
        // not in wx
    }
    

    但是直接在 if 中写这样的表达式太过局限,或者 有很多方式都能判断是在微信环境中,会导致项目中充斥着五花八门的判断,类型谓词的好处就出来了

    function checkIsWxNativeAPICanUse(win: Window): win is { wx: Exclude<Window['wx'], undefined> } & Window {
        return typeof window.wx !== 'undefined'
    }
    // 使用
    if (checkIsWxNativeAPICanUse(window)) {
        window.wx.xxxx()
    }
    

    总结

    非必要少使用 any 既是良好的 ts 代码习惯的养成,也是对自己代码质量的较真

    你还有什么 ts 技巧呢


    起源地下载网 » TypeScript进阶, 如何避免 any

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元