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
