题目描述 🤔
在TypeScript中,实现JavaScript的Array.includes方法,这个类型接受两个参数,返回值为true或者false。
我们需要实现一个类型工具Includes<T, U>,判断类型U是否存在于元组类型T中。
type Result1 = Includes<[1, 2, 3], 2>; // true
type Result2 = Includes<[1, 2, 3], 4> // false
type Result3 = Includes<['a', 'b', 'c'], 'a'> // true
实现思路 💡
type Includes<T extends any[], U> = U extends T[number] ? true : false;
1. 类型参数 📝
T extends any[] // T 必须是一个数组类型
U // U 是要查找的类型
2. T[number] 的含义🎯
T[number] 是TypeScript中的索引访问类型,他会返回数组T中所有可能的元素类型的联合。
例如:
type Arr = [1, 2, 3]
type Union = Arr[number] // Union = 1 | 2 | 3
type StrArr = ['a', 'b', 'c']
type StrUnion = StrArr[number] // StrUnion = 'a' | 'b' | 'c'
3. 条件类型判断 ⚖️
U extends T[number] ? true : false
这是一个条件类型,判断U是否是T[number]的子类型:
- 如果
U能够分配给T[number], 返回true - 否则返回
false
使用示例 🌟
type Includes<T extends any[], U> = U extends T[number] ? true : false
// 示例1:
type Result1 = Includes<[1, 2, 3], 2>
// 解析步骤:
// 1. [1, 2, 3][number] 得到联合类型 1 | 2 | 3
// 2. 2 extends (1 | 2 | 3) 为 true
// 所以 Result1 = true
// 示例2:
type Result2 = Includes<[1, 2, 3], 4>
// 解析步骤:
// 1. [1, 2, 3][number] 得到联合类型 1 | 2 | 3
// 2. 4 extends (1, 2, 3) 为 false
// 所以 Result2 = false
更精确的实现 💡
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest]
? IsEqual<First, U> extends true
? true
: Includes<Rest, U>
: false
IsEqual 代码解析
- 创建函数类型
// 对于类型 X
<T>() => T extends ? 1 : 2
// 对于类型 Y
<T>() => T extends ? 1 : 2
这里创建了两个泛型函数类型,返回1或2。
- 比较这个两个类型函数类型
(<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2)
如果两个类型完全相同,这个表达式才会返回true。
实际例子📝
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
// 例子 1:简单类型
type Case1 = IsEqual<1, 1> // true
type Case2 = IsEqual<1, 2> // false
// 例子 2:any
type Case3 = IsEqual<any, any> // true
type Case4 = IsEqual<any, number> // false
// 例子 3:never
type Case5 = IsEqual<never, never> // true
// 例子 4:对象类型
type Case6 = IsEqual<{ a: 1 }, { a: 1 }> // true
type Case7 = IsEqual<{ a: 1 }, { a: 2 }> // false
以比较1和1为例:
// 步骤1:展开第一个函数类型
<T>() => T extends 1 ? 1 : 2
// 步骤2:展开第二个函数类型
<T>() => T extends 1 ? 2 : 2
// 步骤3:比较这两个函数类型是否完全相同
// 因为它们完全相同,则返回true
为什么这样实现更好💡
- 处理
any类型:
type Test1 = IsEqual<any, number> // false (符合预期)
- 处理
never类型:
type Test2 = IsEqual<never, never> // true (符合预期)
- 精确的类型比较:
type Test3 = IsEqual<{ a: 1 }, { a: 1 }> // true
type Test4 = IsEqual<{ a: 1 }, { a: number }> // false
Includes 实现解析 🎯
type IsEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
type Includes<T extends any[], U> = T extends [infer First, ...infer Rest] ? IsEqual<First, U> extends true ? true : Includes<Rest, U> : false
这个实现方法使用递归方式检查数组。
- 模式匹配
T extends [infer First, ...infer Rest]
First:获取数组第一个元素。Rest:获取剩余元素。
- 递归检查:
IsEqual<First, U> extends true
? true
: Includes<Rest, U> : false
- 比较当前元素,如果不匹配,递归检查剩余元素,当数组为空时放归
false。