TypeScript 双刃剑 “any”:哪里可以用?哪里不能用?

1,236 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

大家好,我是杨成功。

今天想探讨一个话题,就是 TypeScript 中的万能类型 any,我们到底该不该用?如果可以用,哪里可以用?如果不能用,哪里绝不能用?

我们知道 TypeScript 是一个类型检查器,为 javascript 装上了静态类型检查的翅膀,还带着它飞了好久。但是 TypeScript 有时候也给我们带来了不好麻烦,尤其是一些复杂的数据结构,本来是一个简单的获取数组下标,或者获取对象属性,TypeScript 总要给你丢一堆报错。

心态好的同学,找到定义变量的地方,再重新完善一下类型的定义,解决掉问题;心态不好的同学,他娘的我还有业务要写呢,光花时间在定义类型上了,于是一个 any 解决所有问题。

对于祭出 any 大法的同学,我要说,慎重啊!有些地方勉强行,有些地方是真不行,别再任性了!

哪些地方行

先想一下我们用 TypeScript 主要为了解决什么问题,其实很简单也很直接,就是怕你手瓢,莫名其妙变量名写错,或者数组对象操作不严谨,导致线上总报 ReferenceError 的错误。

如果你要组合数据。定义一个临时变量存放数组或对象,这个时候你可以定义 any。比如这样:

var fruits = ['苹果', '葡萄', '香蕉']
const TestArray = ()=> {
    let arr: any[] = []
    fruits.forEach(row=> {
       arr.push({
           value: row, label: row
       })
    })
}

你看这个时候,变量 arr 只是临时变量,如果要严格定义类型的话你非得写一个 interface 不可,不写的话又报错,所以这个地方可以用 any 类型。

除了临时变量,某些函数的参数也可以指定 any,比如一个转换格式的函数:

const ConvertData = (data: any, code: number = 200)=> {
    return { code, data }
}

这个函数就很简单了,参数 data 可能是任意类型,所以直接丢一个 any 是可以的。但是一般通用的工具函数都是需要严格指定参数类型的,不可偷懒。

哪些真不行

如果涉及到全局共用的数据,就不能指定 any 了。这里要特别强调一百遍,接口返回的数据,类型不能用 any!不能用 any!不能用 any!

为啥这么激动?因为这个真的非常重要。其实我认为在我们正常的业务开发场景下,TypeScript 最大的用武之地就是对接口返回的数据(对象和数组)定义类型。因为除了页面排版外,我们绝大部分的工作都是在操作或者使用接口返回的数据。

如果接口返回的数据结构复杂,你在频繁使用的时候非常容易出错,比如某个字段少了一个字母,某个属性少了判空的逻辑,这都不可避免。

但是如果你严格定义了类型,那么我们在任意一个组件里使用这些数据,基本不会出现前面说的这种低级错误,因为 TypeScript 强大的类型提示已经帮你验证了可能的错误隐患。

但如果你非要指定 any,那就芭比Q了。你是免去了定义繁琐的 interface,但 TypeScript 也会跟你闹脾气,不帮你检测类型了,那你还不是容易出错?

除此之外,如果你在写第三方开源库,或者公共函数,那也一定一定不要用 any,因为你的函数是要交给别人使用的,别人很可能传参错误,或者返回错误,这很常见。

如果你没有严格定义类型,用了好多 any,那么别人在使用的时候就得不到正确的传参提示,这样即便使用者书写错误 TypeScript 也无法判断,那么你的代码就不算合格。

总之,涉及到公共使用的代码,函数也好组件也好,一定要严格定义类型!

怎么优雅的定义类型

定义接口返回数据的类型,我一般会在存储数据文件的同级目录下,单独定义一个 types.ts 的文件。

比如我在某个目录下的 index.ts 文件中获取了接口数据:

import axios from 'axios'
import { DateType } from './types'

class OrderStore {
   lists: DateType[] = []
   fetch = async ()=> {
      let res: any = await axiox.get('xxxx')
      this.lists =  res.data || []
   }
}

那么 DateType 这个类型在 types.ts 文件中定义方式如下:

export interface DateType {
  id: number
  area_id: number
  area_name: string
  order_no: string
  order_type: number;
  products_name: string
  sale_price: string
  payable_amount: string
}

这个类型字端根据你接口返回的格式定义,有时候接口返回一大堆,你也不用全都定义在这里,需要用啥字段就定义啥,如果欠缺了后续在这里补充就行。

加我入群

本文来源公众号:程序员成功,这里主要分享前端工程与架构的技术知识。也可以加我微信 ruidoc 进工程与架构主群,与大佬们一同探索学习进步!