[TS 雜談](2) Object object {} 區別及如何辨別 {}(emtyType)

前言

這個是因爲一道題目的思考以及延伸出來的一篇雜談。markdown

題目

實現IsEmptyType<T> 檢查泛型T 是否爲{}框架

栗子

type A = IsEmptyType<string> // false
type B = IsEmptyType<{a: 3}> // false
type C = IsEmptyType<{}> // true
type D = IsEmptyType<any> // false
type E = IsEmptyType<object> // false
type F = IsEmptyType<Object> // false
複製代碼

前置知識

JavaScript 標準內置對象

JS中的全部標準內置對象在TS中都有對應的內置類型,好比ObjectNumberString都會在對應的d.ts中進行聲明,所以Object其實就是內部聲明的一個類型接口(interface),能夠經過ctrl+左鍵點擊Objectvsc中找到對應聲明。oop

image-20210524162520188.png

TS 同名值/同名類型

在上面那張圖中能夠看到declare var Object: ObjectConstructor這個另外一個聲明處,ui

那麼那個declare var Object: ObjectConstructor 又是什麼呢?spa

是看成爲值的時候ObjectObjectConstructor類型。code

TS中同名的類型和同名的值是可以被區分的,orm

而當使用ctrl+左鍵查找來源時會把全部同名的都找出來而後造成這個窗口。對象

栗子

type T = number | string
declare const T: object
type a = T
// ^ type T = string | number
// ^ string | number
console.log(T)
// ^ const T: object
複製代碼

Object object {} 的區別

Object

上面講了其實就是內部的一個類型接口接口

object

object is a type that represents the non-primitive type,ip

i.e. anything that is not number, string, boolean, bigint, symbol, null, or undefined.

除了number, string, boolean, bigint, symbol, null, undefined 類型其餘都算做object類型

即除了基本類型都是object類型(廢話

{}

沒有任何屬性的對象,由於在JS中有包裝類這些概念因此在TS中會有如下現象,

  1. 做爲值的類型,除了null/undefined類型其餘均可以賦值給{}而不報錯

    const c1: {} = 1
    const c2: {} = '1'
    const c3: {} = Symbol()
    const c4: {} = BigInt()
    複製代碼
  2. 做爲類型時,與上面同理

    type t1 = 1 extends {} ? true : false
    type t2 = '1' extends {} ? true : false
    複製代碼

能夠把object看作不能賦值給基本類型的{}

原理

TS中,其實基本類型和對應的內置對象大致一致,只不過在TS顯示時和interface同樣封裝起來了,

type n1 = keyof number
// ^ type n = "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"
type n2 = keyof Number
// ^ keyof Number
type T1 = n1 extends n2 ? true : false // true
type T2 = n2 extends n1 ? true : false // true
複製代碼

看起來彷佛徹底一致,可是呢

const a: Number = 1 // yes
type T1 = Number extends number ? true : false // false
type T2 = number extends Number ? true : false // true
複製代碼

也就是說其實number這個基礎類型實際是在TS內部擴展了Number這個類型接口的,能用基本類型仍是用基本類型,其餘同理。

說是基本類型,其實內部仍是當對象來的。

duck typing

TS類型系統是基於duck typing的思想風格實現的,所以一個對象中就算有多餘的屬性,賦值給一個只須要對象中部分屬性的對象時,是徹底沒問題的,所以上面的

const c1: {} = 1
type T1 = number extends {}  ? true : false // true
複製代碼

徹底沒問題,TS判斷可否賦值就至關於extends結果是否爲true

題解

迴歸題目:實現IsEmptyType<T> 檢查泛型T 是否爲{}

這題考察的其實就是,如何分辨object {} 其餘類型

先搭題目要求的框架,

type IsEmptyType<T> = // ???
複製代碼

很簡單分爲三步:

  1. 分辨並剔除 object:

    前面所知,基本類型不能賦值給object,可是基本類型能夠賦值給{}

    // 即:
    type T1 = number extends object ? true : false // false
    type T2 = number extends {} ? true : false // true
    複製代碼

    所以得出

    type IsEmptyType<T> = number extends T ? true : false
    複製代碼

    如今object就被剔除了,一併剔除的還有許多,如null/undefinde/string

  2. 分辨並剔除其餘類型

    前面有寫到{}類型沒有屬性,因此能夠經過keyof再篩選一層把其餘類型篩掉,

    type IsEmptyType<T> = number extends T  
      ? keyof T extends never    
        ? true		
        : false  
      : false
    複製代碼

    keyof {} never,而此時經過前面的篩選留下的類型只有number/Object/unknown/any/{}幾個類型經過判斷返回true,而知足的keyof typenever的只有null/undefined/unknown/{},所以此次篩選只留下了unknown/{}

  3. 分辨並剔除unknown

    只差最後一步剔除unknowunknow是頂級類型即全部類型的父類,全部類型均可以extends它,所以能夠經過反向思考,寫出

    type IsEmptyType<T> = number extends T  
      ? keyof T extends never    
        ? T extends {}      
          ? true      
          : false    
        : false  
      : false
    複製代碼

    unkown不能 extends其餘類型除了any

結語

img

以爲寫的不錯的話,能夠點個贊麼?

相關文章
相關標籤/搜索