一道字节的 TS 体操面试真题

前天,小册群友问了我一个 TS 体操问题,说是面字节时遇到的。

今天又催了一下:

面试题是这样的:

让实现这个 FormatDate 的类型,用来限制字符串只能是指定的日期格式。

看起来好像没多大难度,就是提取出 YY、MM、DD 和分隔符,然后构造对应的字符串类型就好了。

但上手试了一下,还真没那么简单。

首先,我们用模式匹配的方式,也就是 extends + infer 来提取出 YY、MM、DD 这三部分:

javascript 复制代码
type Seperator = '-' | '.' | '/';

type FormatDate<Pattern extends string> = 
  Pattern extends `${infer Aaa}${Seperator}${infer Bbb}${Seperator}${infer Ccc}`
    ? [Aaa,Bbb,Ccc]
    : never;

同样,也可以提取出分隔符部分:

javascript 复制代码
type FormatDate<Pattern extends string> = 
  Pattern extends `${infer Aaa}${Seperator}${infer Bbb}${Seperator}${infer Ccc}`
    ? Pattern extends `${Aaa}${infer Sep}${Bbb}${infer _}${Ccc}`
      ? [Aaa, Bbb, Ccc, Sep]
      : never
    : never;

然后根据 YY、MM、DD 分别构造 4 位和 2 位的字符串,最后组合起来不就行了?

但问题就在这里。

组合字符串字面量类型是这样写:

javascript 复制代码
type Num = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

type YY = `${Num}${Num}${Num}${Num}`;

type MM = `${Num}${Num}`;

type DD = `${Num}${Num}`;

type GenStr<Type extends string> = 
  Type extends 'YY'
    ? YY
    : Type extends 'MM'
      ? MM
      : DD;

type res3 = `${GenStr<'YY'>}-${GenStr<'MM'>}-${GenStr<'DD'>}`;

就是根据 YY、MM 还是 DD 生成不同的字符串字面量,然后组合到一块。

这时候会提示你 union 数量太多:

因为组合起来的情况太多了。

这时候需要减少 union 数量才行。

所以我们可以改成这样:

javascript 复制代码
type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

type Num2 = Num | 0

type YY = `19${Num2}${Num2}` | `20${Num2}${Num2}`;

type MM = `0${Num}` | `1${0 | 1 | 2}`;

type DD = `${0}${Num}` | `${1 | 2}${Num2}` | `3${0 | 1}`;

type GenStr<Type extends string> = 
  Type extends 'YY'
    ? YY
    : Type extends 'MM'
      ? MM
      : DD;

type res3 = `${GenStr<'YY'>}-${GenStr<'MM'>}-${GenStr<'DD'>}`;

也就是年份只能是 19 和 20 开头,月份只能是 1-12 的数字,日期是 01-31 的数字。

这样,组合就少了很多。

再试下:

现在就能正常计算出类型了。

然后用之前提取出的 Aaa、Bbb、Ccc 和 Sep 来生成字符串字面量类型:

这样,就完成了需求:

回过头来看一下,这个类型难么?

思路并不难,就是通过模式匹配(extends + infer)提取出各部分,然后构造对应的字符串字面量类型,组合起来就好了。

它难在如果直接组合,union 数量会过多,从而报错。

所以需要根据年月日的特点,对生成的字符串字面量类型做更精准的控制。

这样,就能生成满足需求的日期字符串类型。

全部代码如下,大家可以试试:

javascript 复制代码
type Seperator = '-' | '.' | '/';

type Num = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;

type Num2 = Num | 0

type YY = `19${Num2}${Num2}` | `20${Num2}${Num2}`;

type MM = `0${Num}` | `1${0 | 1 | 2}`;

type DD = `${0}${Num}` | `${1 | 2}${Num2}` | `3${0 | 1}`;

type GenStr<Type extends string> = 
  Type extends 'YY'
    ? YY
    : Type extends 'MM'
      ? MM
      : DD;

type FormatDate<Pattern extends string> = 
  Pattern extends `${infer Aaa}${Seperator}${infer Bbb}${Seperator}${infer Ccc}`
    ? Pattern extends `${Aaa}${infer Sep}${Bbb}${infer _}${Ccc}`
      ? `${GenStr<Aaa>}${Sep}${GenStr<Bbb>}${Sep}${GenStr<Ccc>}`
      : never
    : never;

const a: FormatDate<'YY-MM-DD'> = '2023-01-02';

const b: FormatDate<'DD/MM/YY'> = '01/02/2024';

const c: FormatDate<'DD/MM/YY'> = '2024-01-02';

playground 地址

更多内容可以看我的小册:TypeScript 类型体操通关秘籍

总结

今天我们做了一道字节的 ts 体操真题。

核心思路就是模式匹配(extends + infer)提取出各部分内容,然后构造日期字符串。

答出这个,应该就有大部分的分了。

但是如果直接构造,会因为 union 数量太多导致失败。

这时候要根据日期的特点想办法减少 union 的数量,直到可以顺利生成。

再答出这个,这道面试题就稳了。

这道题整体来说还是比较难的,既考察了模式匹配+ 构造的 ts 类型编程基础,又考察了对 union 太多的情况的处理,算是一道比较高阶的面试题。

相关推荐
weixin_382395239 分钟前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__15 分钟前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.1 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~2 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华3 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子5 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅5 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni5 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学7 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端