網站搜尋

typescript - 'unknown' 與 'any'


TypeScript 3.0 根據他們的維基介紹,引入了 unknown 類型:

unknown 現在是一個保留的型別名稱,因為它現在是一個內建型別。根據你對 unknown 的預期使用,你可能想要完全移除該宣告(偏好使用新引入的 unknown 型別),或者將它重新命名為其他名稱。

unknown 和 any 有什麼區別?我們應該在什麼情況下使用 unknown 而不是 any?

答案#1

你可以在 PR 或 RC 公告中閱讀更多關於 unknown 的資訊,但重點是:

[..] unknown 是 any 的類型安全對應。任何東西都可以指派給 unknown,但 unknown 除非透過類型斷言或基於控制流程的縮小,否則不能指派給除了自身和 any 之外的任何東西。同樣地,對 unknown 進行操作之前,必須先斷言或縮小為更具體的類型。

幾個例子:

let vAny: any = 10;          // We can assign anything to any
let vUnknown: unknown =  10; // We can assign anything to unknown just like any 


let s1: string = vAny;     // Any is assignable to anything 
let s2: string = vUnknown; // Invalid; we can't assign vUnknown to any other type (without an explicit assertion)

vAny.method();     // Ok; anything goes with any
vUnknown.method(); // Not ok; we don't know anything about this variable

建議用法如下:

在 TypeScript 中,我們經常會想描述能力最弱的類型。這對於希望傳達「這可以是任何值,因此在使用之前你必須進行某種檢查」的 API 非常有用。這迫使使用者安全地檢查返回的值。

答案#2

它們在語意上是不同的。

unknown 是所有其他類型的父類型。它在類型系統中是一個普通類型。

any 意味著「禁用類型檢查」。它是一個編譯器指令。

答案#3

unknown 和 any 之間的差異描述如下:

就像 any 一樣,任何值都可以指派給 unknown;然而,與 any 不同的是,您無法存取型別為 unknown 的值的任何屬性,也無法呼叫或構造它們。此外,型別為 unknown 的值只能指派給 unknown 或 any。

要回答你關於何時應使用 unknown 而不是 any 的問題:

這對希望表示「這可以是任何值,因此你必須在使用它之前進行某種檢查」的 API 很有用。這迫使用戶安全地檢查返回的值。

查看 TypeScript 3.0 的公告,了解對 unknown 類型變數進行類型檢查的範例以及更詳細的說明。

答案#4

任何,不明:

  • 允許分配任何類型

任何:

  • 允許被指派給任何類型
  • 允許調用任何方法

未知:

  • 不允許被指派給任何類型
  • 不允許呼叫任何方法
const a: any = 'a'; // OK
const b: unknown = 'b' // OK

const v1: string = a; // OK
const v2: string = b; // ERROR
const v3: string = b as string; // OK

a.trim() // OK
b.trim() // ERROR

答案#5

any 類型:

any 類型表示所有可能的 JS 值。每個類型都可以賦值給 any 類型。因此,any 類型是類型系統的通用超類型。TS 編譯器將允許對 any 類型的值進行任何操作。例如:

let myVar: any;

myVar[0];
myVar();
myVar.length;
new myVar();

在許多情況下,這對 TS 編譯器來說過於寬鬆。也就是說,它會允許一些我們本可以知道會導致運行時錯誤的操作。

未知 類型:

unknown 類型表示(就像 any 一樣)所有可能的 JS 值。每種類型都可以分配給 unknown 類型。因此,unknown 類型是類型系統的另一個通用超類型(與 any 並列)。然而,TS 編譯器 不會允許對 unknown 類型的值進行任何操作。此外,unknown 類型僅能分配給 any 類型。一個例子將說明這一點:

let myVar: unknown;

let myVar1: unknown = myVar;   // No error
let myVar2: any = myVar;       // No error
let myVar3: boolean = myVar;   // Type 'unknown' is not assignable to type 'boolean'

// The following operations on myVar all give the error:
// Object is of type 'unknown'
myVar[0];
myVar();
myVar.length;
new myVar();

答案#6

未知

如果你寫了一個函數,僅僅將輸入傳遞給另一個函數,請使用 unknown。從函數的角度來看:「我不知道,我也不想知道」。使用 unknown 沒有任何問題。

例如:

function buy(item: unknown): Purchase {
  if (item) {
    return purchase(item);
  } else {
    throw new TypeError('item is missing');
  }
}

任何

如果你需要對該值調用屬性,那麼 any 更適合。

Linting 可能不喜歡 any,建議你對輸入更具體。這樣,如果你將介面從 isItem 改成 isValid,TypeScript 就會提醒你更新程式碼。

例如:

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
function isItem(item: any): item is Purchase {
  return !!item?.price;
}

呼叫屬性

function isStuff(item: unknown): item is Stuff {
  return (item as Stuff).it !== undefined;
}
function isStuff(item: any): item is Stuff {
  return item.it !== undefined;
}
camelcaseKeys(item) as unknown as Item;

如果你有興趣,可以看看使用者自定義的守衛,我之所以帶上它,是因為這是我少數需要使用的情況之一。

來自 ultimatecourses 的這篇博客:

當沒有其他選擇時,使用 any 類型

很難找到關於 any 的好例子。

答案#7

我雖然遲到了聚會,但會嘗試去揭開它的神秘面紗。

const canBeAnything: any = 100;
const canNotBeAnything: unknown = 100;

// If we try to use a .startsWith() method
canBeAnything.startsWith('10'); // no error
canNotBeAnything.startsWith('10'); // Property 'startsWith' does not exist on type 'unknown'

在 unknown 上使用方法 .startsWith() 的唯一方法是明確告訴編譯器類型,例如

(canNotBeAnything as string).startsWith('10'); // Chill down TS compiler, I know what I am doing. 

後者不會顯示任何編譯錯誤,但它在運行時會拋出錯誤,因為 canNotBeAnything 是數字類型,而我們強制將其轉為 string