Typescript: any和unknown的区别

347 阅读2分钟

any和unknown是typescript中的两种顶级类型,所谓的顶级类型,可以被理解成通用父类型,也就是包含所有类型。

//any
let valueany;

value = true// OK
value = 24// OK
value = "Hello World"// OK
value = []; // OK
value = {}; // OK
value = Math.random// OK
value = null// OK
value = undefined// OK
value = new TypeError(); // OK
value = Symbol("type"); // OK

//unknown
let valueunknown;

value = true// OK
value = 24// OK
value = "Hello World"// OK
value = []; // OK
value = {}; // OK
value = Math.random// OK
value = null// OK
value = undefined// OK
value = new TypeError(); // OK
value = Symbol("type"); // OK

那么问题来了,既然any和unknown都可以表示任意类型,那它们的区别是什么。

  • 任何类型都可以是any类型,any类型就相当于是免检标签,给了开发者很大的自由,typescript允许any类型的值进行任何操作,对它一路绿灯。

  • 任何类型也都可以是unknown类型,但与any完全相反,unknown类型就像是typescript给打上了一个重点检查的标签。在没有对它进行类型检查之前,unknown类型的变量是不能进行任何操作的。

先看个例子:

function invokeCallback(callback: any) {
    try {
        callback();
    } catch (err) {
        console.error(err)
    }
}

invokeCallback(1);

上面的代码,在编译期间不会提示任何错误,但在运行期间会抛出运行时错误,由此可看出,any类型给了开发者人员极大的自由度,但同时也带来了一些隐患。

为了解决 any 类型存在的安全隐患,TypeScript 团队在 3.0 版本时,引入了 unknown 类型,你可以把它理解成类型安全的 any 类型

那么unknown类型的类型安全体现在哪里,我们先看接下来的例子

function invokeCallback(callback: unknown) {
    try {
        // Object is of type 'unknown'.(2571)
        callback(); // Error
    } catch (err) {
        console.error(err)
    }
}

invokeCallback(1);

相比 any 类型,TypeScript 会对 unknown 类型的变量执行类型检查,从而避免出现 callback 参数非函数类型。要解决上述问题,我们需要缩小 callback 参数的类型的范围,即可以通过 typeof 操作符来确保传入的 callback 参数是函数类型的对象:

function invokeCallback(callback: unknown) {
    try {
        if (typeof callback === 'function') {
            callback();
        }
    } catch (err) {
        console.error(err)
    }
}

invokeCallback(1);

我们还可以使用instanceof或用户自定义类型等方式来减小类型的处理范围

declare function isFunction(x: unknown): x is Function;\

function f20(x: unknown) {
    if (x instanceof Error) {
        x;  // Error
    }
    if (isFunction(x)) {
        x;  // Function
    }
}

另外,需要注意的是,unknown 类型的变量只能赋值给 any 类型和 unknown 类型本身。

let valueunknown;

let value1unknown = value; // OK
let value2any = value; // OK
let value3boolean = value; // Error
let value4number = value; // Error
let value5string = value; // Error
let value6object = value; // Error
let value7any[] = value; // Error
let value8Function = value; // Error