这个是因为一道题目的思考以及延伸出来的一篇杂谈。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
。
以为写的不错的话,能够点个赞么?