TypeScript `unknown` 類型


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

--TypeScript 3.0 Release notes - New unknown top type

unknown 類型不能賦值給除了 unknownany 的其他任何類型,使用前必需顯式進行指定類型,或是在有條件判斷情況下能夠隱式地進行類型推斷的情況。

下面代碼是合法的:

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."
}

相關資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM