不如用UnknownScript代替你的AnyScript吧

1,366 阅读3分钟

还是严肃点吧,TypeScript越来越受到开发者们的青睐,但经常看到为了省事而写的 anyunknown 有什么区别呢?(划重点,面试常考喔!)今天就来用实际的例子来看看吧。

先说结论

Type结论
any任何内容都可以被当作 any 类型,而且你可以对 any 类型做任何操作
unknown任何内容都可以被当作 unknown 类型,但在对 unknown 类型做操作/使用之前需要判断其更具体的类型

Talk is cheap, Show me the code

如果这段文字描述无法很好的解决你的疑惑,那么一起看看下边的例子吧。

示例1

let variable: any = undefined;
variable = 10;
variable = false;

let variable: unknown = undefined;
variable = 10;
variable = false;

以上两个代码块都可以成功通过TypeScript的类型检查,也印证了结论中的前半句,任何内容都可以被当作 anyunknown 类型。

示例2

let anyValue: any = 10;
let unknownValue: unknown = 10;

let number1: number = anyValue;
let number2: number = unknownValue; // Error, Type 'unknown' is not assignable to type 'number'.(2322)

可以发现将类型为 unknown 的值赋值给类型类型为 number 的变量时TS的类型检查就会报错了,这也印证了结论中关于unknown的后半句话,在对 unknown 类型做操作/使用之前需要判断其更具体的类型

没问题,将上边的赋值语句稍作修改,成功通过了类型校验。

let number2: number = typeof unknownValue === "number" ? unknownValue : 0;

示例3

// Static Type Checking Phase - All Good!
const magicFunction = (param: any) => {
  console.log(Math.round(param));
  console.log(param.charAt(0));
  console.log(param.push(1));
}

magicFunction(true);

// Runtime
// "Executed JavaScript Failed:" param.charAt is not a function

以上函数在代码编写也就是TypeScript做静态检查期间是不会报出任何错误的,可以看到函数中先后把param当作了数字、字符串、数组来进行操作。

而显然传入的布尔值是无法做这些操作的,对应的错误到了运行阶段才报了出来。 这印证了在声明了param为 any 类型后,TS默认可以对其 做任何操作

而当使用 unknown 替换掉 any

// ⚠ Static Type Checking Phase - Error!

const magicFunction = (param: unknown) => {
  console.log(Math.round(param)); // Argument of type 'unknown' is not assignable to parameter of type 'number'.(2345)
  console.log(param.charAt(0)); // Object is of type 'unknown'.(2571)
  console.log(param.push(1)); // Object is of type 'unknown'.(2571)
}

如果未对param做类型断言,是无法通过TS的类型检查的,这也就是结论中的后半句 unknown 类型做操作/使用之前需要判断其更具体的类型

按照如下方法判断具体类型后,再对相应类型做合法的操作,就可以顺利通过类型校验了

// Static Type Checking Phase - All Good!
const magicFunction = (param: unknown) => {
  if (typeof param === "number") {
    console.log(Math.round(param))
  } else if (typeof param === "string") {
    console.log(param.charAt(0))
  } else if (Array.isArray(param) && param.every(item => typeof item === "number")) {
    console.log(param.push(1));
  }
  console.log("No valid type overlap");
}

magicFunction(true);

// Runtime
// 'No valid type overlap'

总结与思考

  • unknownany 都是 TypeScript 中的顶级类型,而 unknown 可以理解为更安全的 any (如果要对 unknown 执行操作,必须使用类型断言或者缩小到特定的类型)
  • 如果可以的话请使用具体的类型声明,除非真的无法预知传入参数的类型(比如抽象出的公用函数)实在要使用也请优先使用unknown
  • 不要让你的代码变成anyscript,让和你一起开发的伙伴多说 🐂🍺 而不是 “王德发”