簡體   English   中英

打字稿中的運行時類型安全

[英]Runtime typesafety in typescript

我試圖更好地理解 Typescript 中的類型安全,並遇到了我有這個功能的場景:

function test(x: number){
    console.log(typeof x);
}

如果我以這種方式調用此方法 - test('1')它會引發編譯時錯誤,但如果我將其更改為以下內容,則它可以正常工作:

let y: any = '1';
test(y);
//works fine
//Outputs "string"

據我了解,將 'x' 聲明為數字僅在編譯時有效,並且 Typescript 僅強制執行編譯時類型安全而不是運行時。 所以,我想知道我是否理解正確或遺漏了什么,還有哪些不同的方法可以確保運行時類型安全?

TypeScript 是兩種合並的語言:

  1. 類型級語言
  2. 價值層面的語言

第一個在所有類型注釋中都是可見的,它是純 JavaScript 中不存在的語法。 每個類型注釋和保留字,如type, interface, enum, as, in都是類型級語言的一部分。 TS 在編譯過程中首先要做的是檢查類型級語言的語法和語法的正確性以及值級語言的注釋的正確性。

第二個是值級語言,完全正確的JS語法。 它還具有 ECMAScript 提案第 3 階段的大部分功能。

第一部分被完全刪除(Enum 除外,它在運行時具有表示),第二部分保留在運行時中。

回到關於安全的問題,是的 TypeScript 在編寫代碼的過程中確保安全。 您定義合同,編寫合同的轉換,並且 TS 正在檢查與合同注釋相關的代碼的正確性。 它消除了一大堆錯誤,如拼寫錯誤,或使用空/未定義對象的方法和屬性,它還在程序流程中提供了數據的可見定義。

但是,它不保護運行時。 所有類型注解都只是假設,如果我們定義API具有端點響應的這樣那樣的結構,那么TS會保證代碼會遵循這種結構,但是如果在運行時,結構會有所不同,程序自然會失敗,因為合同不等於數據。

回到你的例子

function test(x: number){
    console.log(typeof x);
}

將函數test定義為一個以number為參數的函數,你說的沒什么不同,然后number將被傳遞給函數。 所以上面的實現實際上是一個常量,因為typeof x將始終返回number ,因為這正是注釋所說的。

// below is equal to your implementation, as number has one specific type
// caution is equal for TS, not for the runtime!
function test() {
  return console.log('number')
}

如果函數在輸入方面是多態的,那么輸入應該被這樣注釋。 可能會發生您不知道可以獲得什么輸入的情況,然后您可以實施結構檢查。 它的正確名稱是-type guard 考慮下面的例子

function test(x: unknown) {
  if (typeof x === 'number') {
    return x * 2; // return number
  }
  if (typeof x === 'string') {
    return x + '- I am string'; // return number
  }

  if (typeof x === 'object') {
    return x; // return object;
  }

  return null; // for others return null
}

現在函數test將輸出推斷為聯合string | number | object | null string | number | object | null string | number | object | null 通過使用控制流和條件,TS 能夠理解函數返回的內容。

每次您的代碼處理一些多態類型時,您都可以使用類型保護來准確指定您正在使用的類型。 檢查由結構完成(因為運行時只存在結構,類型只在代碼編寫時注釋結構),因此您可以檢查typeofinstanceof或者對象是否具有特定的鍵或值。

非常重要的一點要記住 -類型是一些現有運行時結構的標簽。 標簽/類型在運行時不存在,但結構存在。 這就是 TS 能夠理解類型保護的原因,因為每種類型都指代某種結構。

運行時類型安全與 TypeScript 無關。 TypeScript 團隊非常清楚該語言的目標,您可以在 wiki的“非目標部分閱讀以下行:

提供額外的運行時功能或庫。

如果您正在尋找運行時類型安全,您將不得不尋找其他地方。

最重要的是,您通過聲明它的類型為any來主動禁用您可能想要的類型檢查。 您可以在手冊中閱讀有關該行為的信息

TS 中的運行時類型檢查策略與 JS 中的相同,因為在運行時,不再有任何 TS,全是 JavaScript。

當您說let y: any = '1'您基本上關閉了對變量y類型檢查 - 因此您應該將其傳遞給需要數字的方法。 如果不添加any類型,它將在編譯時拋出錯誤。 基本上你是對的, :type注釋適用於編譯時類型檢查。 檢查打字稿游樂場TSC 如何編譯您的代碼可能會很有趣。

TypeScript 僅在編譯時保證類型檢查,這是真的。

JavaScript 具有動態類型,因此您可以使用typeof自己檢查它, if但沒有人使用 typescript 來檢查它。

您也可以在tsconfig.json啟用所有strict選項,ts 將加強驗證規則。

更新

您還可以使用tslinttslint-eslint-rules包進行代碼驗證,包括no-any等類型規則

JavaScript 有一個斷言函數console.assert(assertion, message?) 我通常在我的函數中使用它:

/**
 * Checks if the value is in range
 *
 * @param value
 * @param fromInclude lower inclusive limit
 * @param toExclude upper exclusive limit
 */
export function inRange(value: number, fromInclude: number, toExclude: number): boolean {
    console.assert(!isNaN(value));
    console.assert(!isNaN(fromInclude));
    console.assert(!isNaN(toExclude));
    console.assert(fromInclude < toExclude);

    return value >= fromInclude && value < toExclude;
}

如果出現問題,這非常適合讓開發人員盡早知道。 並且您可以配置您的構建管道以刪除生產構建中的所有console.assert調用,因此它更輕,並且不會在控制台中向您的 prod 用戶顯示警告。 您可以在斷言中進行所有類型檢查,因為許多類型的類型轉換不會導致錯誤,但很高興知道您在某處獲得字符串'237'而不是數字237並修復此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM