unknown拯救anyScript

294 阅读3分钟

anyScript 🤡

有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用any类型来标记这些变量

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean

当你只知道一部分数据的类型时,any类型也是有用的

let list: any[] = [1, true, "free"];

list[1] = 100;

除了这些场景,滥用any会让我们的ts代码变成anyScript

unknown 😈

TypeScript 3.0引入了一个顶级的unknown类型。 对照于anyunknown是类型安全的。 任何值都可以赋给unknown,但是当没有类型断言或基于控制流的类型细化时unknown不可以赋值给其它类型,除了它自己和any外。 同样地,在unknown没有被断言或细化到一个确切类型之前,是不允许在其上进行任何操作的。

赋值操作 🧐

let baz: unknown = 123;
const notSure: any = '123';
baz = '123'; // 🆗
baz = null; // 🆗
baz = undefined; // 🆗
baz = { 1: 1, 2: 2, 3: 3 }; // 🆗
baz = [1, '2', Symbol(3)]; // 🆗
baz = [1, 2, 3]; // 🆗
baz = notSure; // 🆗

const baz: unknown = 123;
console.log(baz.info) // ✖️
const V1: any = baz; // 🆗
const V2: unknown = baz; // 🆗
const V3: string = baz; // 🆗
  • baz是unknown类型,对其进行任意类型的赋值都是合法的。
  • unknown没有被断言或细化到一个确切类型之前,是不允许在其上进行任何操作的(包括调用它,构造它,访问它的属性)
  • 当没有类型断言或基于控制流的类型细化时unknown不可以赋值给其它类型,除了它自己和any

联合类型 🧐

// In a union an unknown absorbs everything

type T10 = unknown | null;  // unknown
type T11 = unknown | undefined;  // unknown
type T12 = unknown | null | undefined;  // unknown
type T13 = unknown | string;  // unknown
type T14 = unknown | string[];  // unknown
type T15 = unknown | unknown;  // unknown
type T16 = unknown | any;  // any
  • 在联合类型中unknown会吸收除了any的任何类型,。
  • any会吸收任意类型,如果任一组成类型是any,则联合类型相当于any

交叉类型 🧐

// In an intersection everything absorbs unknown

type T00 = unknown & null; // null
type T01 = unknown & undefined; // undefined
type T02 = unknown & null & undefined; // null & undefined (which becomes never)
type T03 = unknown & string; // string
type T04 = unknown & string[]; // string[]
type T05 = unknown & unknown; // unknown
type T06 = unknown & any; // any
  • 在交叉类型中包含unknown不会改变结果。

如何使用unkown 🥴

  • 使用 typeofinstanceof 缩窄变量的类型,直至细化到某一个确切类型
export const invokeCallback = (callback: unknown) => {
  try {
    // ✖️ 对象的类型为 "unknown"。
    callback()
    if (typeof callback === 'function') {
      callback();
    }
  } catch (error) {
    console.log(error);
  }
};
declare function isFunction(x: unknown): x is Function;
const fn = (x: unknown) => {
  if (x instanceof Error) {
    x; // Error
  }
  if (isFunction(x)) {
    x; // Function
  }
};

总结

any 和 unknown 都是顶级类型,但是 unknown 更加严格,不像 any 那样不做类型检查,反而 unknown 因为未知性质,不允许访问属性,不允许赋值给其他有明确类型的变量。

可以将unknown理解为类型安全的any。

所以在工作中,为了使用ts的错误检查和编译器支持,我们应该尽量使用unknown来解决any带来的安全隐患