欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

TypeScript?背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解

 更新時間:2022年11月23日 11:33:34   作者:草帽Plasticine  
這篇文章主要為大家介紹了TypeScript?背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

你能說清楚類型、類型系統(tǒng)、類型檢查這三個的區(qū)別嗎?在理解TypeScript的結(jié)構(gòu)化類型系統(tǒng)之前,我們首先要搞清楚這三個概念和它們之間的關(guān)系

  • 類型:即對變量的訪問限制賦值限制。如 TypeScript 中的原始類型、對象類型、函數(shù)類型和字面量類型等類型,當一個變量類型確定后,你不能訪問這個類型中不存在的屬性或方法,也不能將其他不兼容的類型的變量賦給該變量
  • 類型系統(tǒng):本質(zhì)上是一組規(guī)則,其描述如何為變量、函數(shù)等結(jié)構(gòu)分配和實施類型。同時還定義了如何判斷類型之間的兼容性,也正是我們今天主要討論的概念
  • 類型檢查:是一種能力,一種確保類型遵循類型系統(tǒng)下的類型兼容性的能力

理解它們對理解我們今天要討論的TypeScript的結(jié)構(gòu)化類型系統(tǒng)很有幫助

類型系統(tǒng)分為結(jié)構(gòu)化類型系統(tǒng)和標稱類型系統(tǒng),首先我們來看看它們分別都是什么

什么是結(jié)構(gòu)化類型系統(tǒng)?

基于類型結(jié)構(gòu)進行類型兼容性判斷

關(guān)鍵體現(xiàn)在兩個類型的比較當中,當兩個類型比較時,如果是按照屬性和方法是否相同來比較的話就稱為結(jié)構(gòu)化類型系統(tǒng),也叫鴨子類型。

比如下面這個例子:

class Dog {
  say() {
    console.log('wang wang wang!')
  }
}
class Cat {
  say() {
    console.log('miao miao miao!')
  }
}
const invokeSay = (dog: Dog) => {
  dog.say()
}
const dog = new Dog()
const cat = new Cat()
invokeSay(dog) // wang wang wang!
invokeSay(cat) // miao miao miao!

雖然invokeSay函數(shù)接收的參數(shù)類型是Dog,但是由于Cat類型的結(jié)構(gòu)和Dog是一樣的(都是只有一個 say 方法),因此會被認為是同一種類型,這就是結(jié)構(gòu)化類型的特點,基于類型結(jié)構(gòu)進行類型兼容性判斷

代表語言:C#、Python、Objective-C

什么是標稱類型系統(tǒng)?

基于類型名進行兼容性判斷

與結(jié)構(gòu)化類型系統(tǒng)相對的叫標稱類型系統(tǒng),它在判斷兩個類型是否相同時,只看它們的名稱是否相同,即便內(nèi)部結(jié)構(gòu)完全相同也不能認為是同一種類型,比如下面這個例子:

/** @description 人民幣 */
type CNY = number
/** @description 美元 */
type USD = number
const CNYCount: CNY = 666
const USDCount: USD = 333
const addCount = (source: CNY, input: CNY) => source + input
addCount(CNYCount, USDCount)

在標稱類型系統(tǒng)中,這里對于addCount的調(diào)用是錯誤的,盡管CNY和USD類型都是number類型,但由于它們的名稱不同,因此被視為是不同類型

代表語言:C++、Java、Rust

結(jié)構(gòu)化類型系統(tǒng)等價于鴨子類型系統(tǒng)嗎?

嚴格意義上兩者并不等同

  • 結(jié)構(gòu)化類型系統(tǒng)基于完全的類型結(jié)構(gòu)來判斷類型兼容性
  • 鴨子類型系統(tǒng)基于運行時訪問的部分來判斷類型兼容性

TypeScript 本身并不是在運行時進行類型檢查,因此并不嚴格等價于鴨子類型系統(tǒng)

如何在 TypeScript 中模擬標稱類型系統(tǒng)?

由于 TypeScript 的類型系統(tǒng)是結(jié)構(gòu)化類型系統(tǒng),所以剛剛那個例子在 TypeScript 中是可以正常運行的:

/** @description 人民幣 */
type CNY = number
/** @description 美元 */
type USD = number
const CNYCount: CNY = 666
const USDCount: USD = 333
const addCount = (source: CNY, input: CNY) => source + input
addCount(CNYCount, USDCount)

這里我們的意圖應(yīng)當是讓人民幣 CNY 和美元 USD 作為兩種不同的類型,但是由于 TypeScript 結(jié)構(gòu)化類型系統(tǒng)的特性,兩個類型本質(zhì)上都是number類型,因此會被認為是同一個類型

而如果是標稱類型系統(tǒng)就不會有這個問題,如果我們能在 TypeScript 中模擬實現(xiàn)標稱類型系統(tǒng)就符合預(yù)期了,那么要怎么模擬呢?

對比結(jié)構(gòu)化類型系統(tǒng)和標稱類型系統(tǒng)的特點,只要我們能夠在兩個完全兼容的結(jié)構(gòu)化類型中加入一個標識符,那么即便這兩個類型的結(jié)構(gòu)是兼容的,但由于這個標識符并不相同,因而會被認為是兩個不同的類型

利用這個特點我們就可以在 TypeScript 中模擬標稱類型系統(tǒng)

以下有兩種方式模擬實現(xiàn)標稱類型系統(tǒng):

  • 通過交叉類型實現(xiàn) -- 只能在類型層面上實現(xiàn),無法在運行時邏輯上實現(xiàn)
  • 通過類實現(xiàn) -- 能兼顧類型層面和運行時邏輯層面

交叉類型實現(xiàn)

我們可以實現(xiàn)一個工具類型Nominal,對傳入的泛型參數(shù)進行處理,將其和一個特殊的類型進行交叉類型操作,上面提到的標識符就是由這個特殊的類型提供的

nominal.ts

class TagProtector<T extends string> {
  protected __tag__: T
}
export type Nominal<T, U extends string> = T & TagProtector<U>
index.ts
import { Nominal } from './nominal'
/** @description 人民幣 */
type CNY = Nominal<number, 'CNY'>
/** @description 美元 */
type USD = Nominal<number, 'USD'>
const CNYCount = 666 as CNY
const USDCount = 333 as USD
const addCount = (source: CNY, input: CNY) => source + input
// 類型“USD”的參數(shù)不能賦給類型“CNY”的參數(shù)。
//   不能將類型“USD”分配給類型“TagProtector<"CNY">”。
//     屬性“__tag__”的類型不兼容。
//       不能將類型“"USD"”分配給類型“"CNY"”。ts(2345)
addCount(CNYCount, USDCount)

由于 TypeScript 實際運行時還是以 JavaScript 的方式運行的,所以類型代碼會被抹除,抹除類型后這個代碼仍然能夠正常執(zhí)行

這是因為通過這種方式模擬實現(xiàn)標稱類型系統(tǒng)只能在類型層面模擬,實際的運行時并不能起到檢查的作用,這時候就要用下面的方案 -- 用類模擬實現(xiàn)

類實現(xiàn)

/** @description 人民幣 */
class CNY {
  private __tag__!: void
  constructor(public value: number) {}
}
/** @description 美元 */
class USD {
  private __tag__!: void
  constructor(public value: number) {}
}
const CNYCount = new CNY(666)
const USDCount = new USD(666)
const addCount = (source: CNY, input: CNY) => source.value + input.value
// 類型“USD”的參數(shù)不能賦給類型“CNY”的參數(shù)。
//   類型具有私有屬性“__tag__”的單獨聲明。
addCount(CNYCount, USDCount)

以上兩種方式本質(zhì)都是通過一個非公開的額外屬性對類型添加了額外的標識符,從而能夠讓結(jié)構(gòu)化類型系統(tǒng)將它們判斷為不同的類型

總結(jié)

相信現(xiàn)在你能夠理解什么是結(jié)構(gòu)化類型系統(tǒng)了,正如開頭介紹的類型系統(tǒng)的概念中所說,它定義了如何判斷類型之間的兼容性

而結(jié)構(gòu)化類型系統(tǒng)對于類型之間兼容性的判斷則是基于類型的結(jié)構(gòu)來判斷的,只要兩個類型的結(jié)構(gòu)上可兼容(如一個類型中的所有屬性和方法在另一個類型中都存在),則可以將兩個類型視為是兼容的

除此之外,我們還了解了與結(jié)構(gòu)化類型系統(tǒng)對應(yīng)的標稱類型系統(tǒng),并且了解到如何在TypeScript中模擬實現(xiàn)標稱類型系統(tǒng),讓我們對結(jié)構(gòu)化類型系統(tǒng)有更深刻的理解

以上就是TypeScript 背后的結(jié)構(gòu)化類型系統(tǒng)原理詳解的詳細內(nèi)容,更多關(guān)于TypeScript 結(jié)構(gòu)化類型系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論