# 14-第一个元素
# 题目描述
实现一个通用First<T>
,它接受一个数组T
并返回它的第一个元素的类型。
例如:
type arr1 = ['a', 'b', 'c'];
type arr2 = [3, 2, 1];
type head1 = First<arr1>; // expected to be 'a'
type head2 = First<arr2>; // expected to be 3
# 分析
这一题主要是围绕元组的,要获取元组的第一个,就必须要借助 ts 的 infer
了。
这一类题的套路都如下:T extends [infer F, ...infer R] ? F : never
。中级题最后一个元素也是一模一样的思路。
其本质可以说是模式匹配,如果 T
能够匹配上 [infer F, ...infer R]
,那么就取前一个类型,否则走后者,基本逻辑和 js 中的三元表达式一样。
关于 extends
这种三元表达式的用法,可以参考 conditional-types (opens new window)。
# 题解
了解了 infer 和 extends 用法后,其实答案也非常简单了:
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils';
type cases = [
Expect<Equal<First<[3, 2, 1]>, 3>>,
Expect<Equal<First<[() => 123, { a: string }]>, () => 123>>,
Expect<Equal<First<[]>, never>>,
Expect<Equal<First<[undefined]>, undefined>>,
];
type errors = [
// @ts-expect-error
First<'notArray'>,
// @ts-expect-error
First<{ 0: 'arrayLike' }>,
];
// ============= Your Code Here =============
type First<T extends any[]> = T extends [infer F, ...infer _] ? F : never;
这里值得一提的就是 [] extends [infer F, ...infer R]
的判断,由于有两个变量需要推断,F
和 R
,但是原数组中一个元素都没有,此时就会走 false 的逻辑,返回 never。而如果有一个元素,那么还是会走 true 的逻辑,此时 R
会被推断为 []
。
# 知识点
xxx extends infer xxx ? A : B
,infer + extends 范式[infer F, ...infer R]
中,如果 1 个元素都没有,那么走 false 的逻辑
# 其他解题思路
上面的解题思路是利用infer
进行匹配, 还有一种完全不同的思路, 是利用元组的索引特性, 我们通过T[number]
可以获取任意位置的元素, 还能通过T['length']
获取元组的长度, 类比于 js 中国呢我们获取第一个元素的方式, 我们可以写出如下伪代码:
// 利用isEmpty判断数组是否为空
Array.isEmpty(arr) ? undefined : arr[0];
// 或者利用length判断数组是否为空
arr.length === 0 ? undefined : arr[0];
将上述伪代码改写成 typescript 类型版本:
type First<T extends any[]> = T['length'] extends 0 ? never : T[0];
// 或者
type First<T extends any[]> = T extends [] ? never : T[0];
← 11-元组转换为对象 18-获取元组长度 →