這個是因爲一道題目的思考以及延伸出來的一篇雜談。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
複製代碼
JS中的全部標準內置對象在TS中都有對應的內置類型,好比Object
,Number
,String
都會在對應的d.ts
中進行聲明,所以Object
其實就是內部聲明的一個類型接口(interface
),能夠經過ctrl+左鍵點擊Object
在vsc
中找到對應聲明。oop
在上面那張圖中能夠看到declare var Object: ObjectConstructor
這個另外一個聲明處,ui
那麼那個declare var Object: ObjectConstructor
又是什麼呢?spa
是看成爲值的時候Object
是ObjectConstructor
類型。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
is a type that represents the non-primitive type,ipi.e. anything that is not
number
,string
,boolean
,bigint
,symbol
,null
, orundefined
.
除了number
, string
, boolean
, bigint
, symbol
, null
, undefined
類型其餘都算做object
類型
即除了基本類型都是object
類型(廢話
沒有任何屬性的對象,由於在JS
中有包裝類這些概念因此在TS
中會有如下現象,
做爲值的類型,除了null/undefined類型其餘均可以賦值給{}而不報錯
const c1: {} = 1
const c2: {} = '1'
const c3: {} = Symbol()
const c4: {} = BigInt()
複製代碼
做爲類型時,與上面同理
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
這個類型接口的,能用基本類型仍是用基本類型,其餘同理。
說是基本類型,其實內部仍是當對象來的。
TS
類型系統是基於duck typing的思想風格實現的,所以一個對象中就算有多餘的屬性,賦值給一個只須要對象中部分屬性的對象時,是徹底沒問題的,所以上面的
const c1: {} = 1
type T1 = number extends {} ? true : false // true
複製代碼
徹底沒問題,TS
判斷可否賦值就至關於extends
結果是否爲true
。
迴歸題目:實現IsEmptyType<T>
檢查泛型T
是否爲{}
。
這題考察的其實就是,如何分辨object {} 其餘類型
。
先搭題目要求的框架,
type IsEmptyType<T> = // ???
複製代碼
很簡單分爲三步:
分辨並剔除 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
。
分辨並剔除其餘類型
前面有寫到{}
類型沒有屬性,因此能夠經過keyof
再篩選一層把其餘類型篩掉,
type IsEmptyType<T> = number extends T
? keyof T extends never
? true
: false
: false
複製代碼
keyof {}
爲never
,而此時經過前面的篩選留下的類型只有number/Object/unknown/any/{}
幾個類型經過判斷返回true
,而知足的keyof type
爲never
的只有null/undefined/unknown/{}
,所以此次篩選只留下了unknown/{}
分辨並剔除unknown
只差最後一步剔除unknow
,unknow
是頂級類型即全部類型的父類,全部類型均可以extends
它,所以能夠經過反向思考,寫出
type IsEmptyType<T> = number extends T
? keyof T extends never
? T extends {}
? true
: false
: false
: false
複製代碼
unkown
不能 extends
其餘類型除了any
。
以爲寫的不錯的話,能夠點個贊麼?