unknown
字面理解和 any
其實沒差,任何類型都可賦值給它,但有一點,
Anything is assignable to unknown, but unknown isn’t assignable to anything but itself and any without a type assertion or a control flow based narrowing
unknown
類型不能賦值給除了 unknown
或 any
的其他任何類型,使用前必需顯式進行指定類型,或是在有條件判斷情況下能夠隱式地進行類型推斷的情況。
下面代碼是合法的:
let a: unknown;
const b: unknown = a;
const c: any = a;
因為 unknown
是可以賦值給 unknown
的,而下面的代碼則不行,
let a: unknown;
// 🚨Type 'unknown' is not assignable to type 'number'.ts(2322)
const b: number = a;
但是如果使用時,明確知道了類型,則可以這樣來修正:
let a: unknown;
// 🚨Type 'unknown' is not assignable to type 'number'.ts(2322)
const b: number = a;
或者在條件語句中,已經可以明確推斷出類型:
let a: unknown;
let b: number = <number>a;
function isNumber(val: any): val is number {
return typeof val === "number";
}
if (isNumber(a)) {
b = a;
}
所以在使用時,unknown
類型會比 any
更加安全。這個安全體現在,雖然它和 any
一樣存儲了任意類型的值,但是具體使用的時候,這個類型需要顯式確定,由使用者進行指定將 unknown
轉換成某一確定類型。
優先級
與正交類型的搭配
正交類型(intersection type)中,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
與聯合類型的搭配
聯合類型(union type)中 unknown
起絕對作用:
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
上面僅一個例外,及和 any
組成的聯合類型,最終結果是 any
。
使用在條件類型中
條件類型(conditional type)中,
type T30<T> = unknown extends T ? true : false; // Deferred
type T31<T> = T extends unknown ? true : false; // Deferred (so it distributes)
對於上面的條件類型,進行以下測試:
// `unknown` 不能賦值給 `number`
type foo = T30<number>; // false
// `unknown` 可以賦值給 `any`
type bar = T30<any>; // true
// 任何類型都可賦值給 unknown
,所以都為 true
type a = T31<number>; // true
type b = T31<any>; // true
可進行的操作
只能進行等於的判斷,其他操作則會報錯。
function f10(x: unknown) {
x == 5;
x !== 10;
x >= 0; // Error
x + 1; // Error
x * 2; // Error
-x; // Error
+x; // Error
}
屬性字段獲取,方法調用等,也是不允許的:
function f11(x: unknown) {
x.foo; // Error
x[5]; // Error
x(); // Error
new x(); // Error
}
當解構中有 unknown
類型時,會導致解構出來的結果也是 unknown
。
function f26(x: {}, y: unknown, z: any) {
let o1 = { a: 42, ...x }; // { a: number }
let o2 = { a: 42, ...x, ...y }; // unknown
let o3 = { a: 42, ...x, ...y, ...z }; // any
}
具體使用場景
unknown
用於變量類型不確定,但肯定可以確定的情形下,比如下面這個示例中,入參總歸會有個值,根據這個值的類型進行不同的處理,這里使用 unknown
替代 any
則會更加類型安全。
function prettyPrint(x: unknown): string {
if (Array.isArray(x)) {
return "[" + x.map(prettyPrint).join(", ") + "]"
}
if (typeof x === "string") {
return `"${x}"`
}
if (typeof x === "number") {
return String(x)
}
return "etc."
}