分享一道不错的TS面试题(含3层),看看能答到第几层!

分享一道不错的TS面试题(含3层),看看能答到第几层!

最近遇见一道不错的 TS 面试题,分享一下。

这道题有 3 个层次,我们一层层来看。

第一层的要求是这样的:

实现一个 zip 函数,对两个数组的元素按顺序两两合并,比如输入 [1,2,3], [4,5,6] 时,返回 [[1,4], [2,5],[3,6]]

这层就是每次各从两个数组取一个元素,合并之后放到数组里,然后继续处理下一个,递归进行这个流程,直到数组为空即可。

function zip(target, source) {  if (!target.length || !source.length) return [];  const [one, ...rest1] = target;  const [other, ...rest2] = source;  return [[one, other], ...zip(rest1, rest2)];}

结果是对的:

分享一道不错的TS面试题(含3层),看看能答到第几层!

第一层还是比较简单的,然后我们来看第二层要求:

给这个 zip 函数定义 ts 类型(两种写法)

函数的定义有两种形式:

直接通过 function 声明函数:

function func() {}

和声明匿名函数然后赋值给变量:

const func = () => {}

而参数和返回值的类型都是数组,只是具体类型不知道,可以写 unknown[]。

所以两种函数类型的定义就是这样的:

分享一道不错的TS面试题(含3层),看看能答到第几层!

分享一道不错的TS面试题(含3层),看看能答到第几层!

也是直接 function 声明函数类型和 interface 声明函数类型然后加到变量类型上两种。

因为具体元素类型不知道,所以用 unknown。

这里可能会问 any 和 unknown 的区别:

any 和 unknown 都可以接收任何类型:

分享一道不错的TS面试题(含3层),看看能答到第几层!

但是 any 也可以赋值给任何类型,但 unknown 不行。

分享一道不错的TS面试题(含3层),看看能答到第几层!

分享一道不错的TS面试题(含3层),看看能答到第几层!

这里只是用来接收其他类型, 所以 unknown 比any 更合适一些,更安全。

这一层也是比较基础的 ts 语法,第三层就上了难度了:

用类型编程实现精确的类型提示,比如参数传入 [1,2,3], [4,5,6],那返回值的类型要提示出 [[1,4], [2,5],[3,6]]

这里要求返回值类型是精确的,我们就要根据参数的类型来动态生成返回值类型。

也就是这样:

分享一道不错的TS面试题(含3层),看看能答到第几层!

声明两个类型参数 Target、Source,约束为 unknown[],也就是元素类型任意的数组类型。

这俩类型参数分别是传入的两个参数的类型。

返回值通过 Zip 计算得出。

然后要实现 Zip 的高级类型:

传入的类型参数分别是两个数组类型,我们同样要从中提取出每个元素合并到一起。

提取元素可以用模式匹配的方式:

分享一道不错的TS面试题(含3层),看看能答到第几层!

分享一道不错的TS面试题(含3层),看看能答到第几层!

所以这个类型就可以这样定义:

type Zip =    One extends [infer OneFirst,...infer Rest1]      ? Other extends [infer OtherFirst, ...infer Rest2]        ? [[OneFirst, OtherFirst], ...Zip]        : []      : [];

分别提取两个数组的第一个元素,构造成新数组。然后对剩下的数组递归进行这样的处理,直到数组为空。

这样就实现了我们想要的高级类型:

分享一道不错的TS面试题(含3层),看看能答到第几层!

但你把它作为返回值加到函数上会报错:

分享一道不错的TS面试题(含3层),看看能答到第几层!

因为声明函数的时候都不知道参数是啥,自然计算不出 Zip 的值,所以这里会类型不匹配:

分享一道不错的TS面试题(含3层),看看能答到第几层!

那怎么办呢?

可以用函数重载解决:

分享一道不错的TS面试题(含3层),看看能答到第几层!

ts 支持函数重载,可以写多个同名函数的类型的类型定义,最后写函数的实现,这样用到这个函数的时候会根据参数的类型来匹配函数类型。

我们用了类型编程的那个函数通过这种方式写就不会报错了。

我们使用下看看:

分享一道不错的TS面试题(含3层),看看能答到第几层!

咋返回值的类型不对呢?

分享一道不错的TS面试题(含3层),看看能答到第几层!

其实这时候匹配的函数类型是对的,只不过推导出的不是字面量类型。

白瓜面试 白瓜面试

白瓜面试 – AI面试助手,辅助笔试面试神器

白瓜面试 40 查看详情 白瓜面试

这时候可以加个 as const。

分享一道不错的TS面试题(含3层),看看能答到第几层!

但是加上 as const 会推导出 readonly [1,2,3]

分享一道不错的TS面试题(含3层),看看能答到第几层!

这样类型就不匹配了,所以要在类型参数的声明上也加上 readonly:

分享一道不错的TS面试题(含3层),看看能答到第几层!

但这样 Zip 函数的类型又不匹配了。

难道要把所有用到这个类型的地方都加上 readonly 么?

不用,我们 readonly 的修饰去掉不就行了?

Typescript 有内置的高级类型 readonly:

分享一道不错的TS面试题(含3层),看看能答到第几层!

可以把索引类型的每个索引都加上 readonly 修饰:

分享一道不错的TS面试题(含3层),看看能答到第几层!

但没有提供去掉 readonly 修饰的高级类型,我们可以自己实现一下:

分享一道不错的TS面试题(含3层),看看能答到第几层!

用映射类型的语法构造个新索引类型,加上个 -readonly 就是去掉 readonly 修饰的意思。

分享一道不错的TS面试题(含3层),看看能答到第几层!

有的同学可能问了,数组类型也是索引类型么?

是,索引类型是聚合多个元素的类型,所以对象、数组、class 都是。

所以我们把它用在数组上自然也是可以的:

分享一道不错的TS面试题(含3层),看看能答到第几层!

(准确来说叫元组,元组是元素个数固定的数组)

那我们只要在传入 Zip 之前,用 Mutable 去掉 readonly 就可以了:

分享一道不错的TS面试题(含3层),看看能答到第几层!

再来试一下:

分享一道不错的TS面试题(含3层),看看能答到第几层!

大功告成!现在返回值的类型就对了。

但还有个问题,如果不是直接传入字面量,是推导不出字面量类型的,这时候貌似就不对了:

分享一道不错的TS面试题(含3层),看看能答到第几层!

可我们不都声明重载类型了么?

如果推导不出字面量类型,应该匹配这个呀:

分享一道不错的TS面试题(含3层),看看能答到第几层!

但实际上它匹配的还是第一个:

分享一道不错的TS面试题(含3层),看看能答到第几层!

这时候其实只要调换下两个函数类型的顺序就可以了:

分享一道不错的TS面试题(含3层),看看能答到第几层!

分享一道不错的TS面试题(含3层),看看能答到第几层!

这时字面量参数的情况依然也是对的:

分享一道不错的TS面试题(含3层),看看能答到第几层!

为什么呢?

因为重载函数的类型是从上到下依次匹配,只要匹配到一个就应用。

非字面量的情况,类型是 number[],能匹配 unknown[] 的那个类型,所以那个函数类型生效了。

分享一道不错的TS面试题(含3层),看看能答到第几层!

而字面量的情况,推导出的是 readonly [1,2,3],带有 readonly 所以不匹配 unknown[],继续往下匹配,就匹配到了带有类型参数的那个函数类型。

这样两种情况就都应用了合适的函数类型。

全部代码是这样的:

type Zip = One extends [  infer OneFirst,  ...infer Rest1]  ? Other extends [infer OtherFirst, ...infer Rest2]    ? [[OneFirst, OtherFirst], ...Zip]    : []  : [];type Mutable = {  -readonly [Key in keyof Obj]: Obj[Key];};function zip(target: unknown[], source: unknown[]): unknown[];function zip(  target: Target,  source: Source): Zip<Mutable, Mutable>;function zip(target: unknown[], source: unknown[]) {  if (!target.length || !source.length) return [];  const [one, ...rest1] = target;  const [other, ...rest2] = source;  return [[one, other], ...zip(rest1, rest2)];}const result = zip([1, 2, 3] as const, [4, 5, 6] as const);const arr1 = [1, 2, 3];const arr2 = [4, '5', 6];const result2 = zip(arr1, arr2);

ts playground 地址

总结

今天我们做了一道综合的 ts 面试题,一共有三层:

第一层实现 js 的逻辑,用递归或者循环都能实现。

第二层给函数加上类型,用 function 声明类型和 interface 声明函数类型两种方式,参数和返回值都是 unknown[]。

第三层是用类型编程实现精准的类型提示,这一层需要拿到参数的类型,通过提取元素的类型并构造出新的数组类型返回。还要通过函数重载的方式来声明类型,并且要注意重载类型的声明顺序。

as const 能够让字面量推导出字面量类型,但会带有 readonly 修饰,可以自己写映射类型来去掉这个修饰。

其实这也是我们学习 ts 的顺序,我们先要能把 js 逻辑写出来,然后知道怎么给函数、class 等加 ts 类型,之后学习类型编程,知道怎么动态生成类型。

其中类型编程是 ts 最难的部分,也是最强大的部分。攻克了这一层,ts 就可以说学的差不多了。

【相关推荐:javascript学习教程

以上就是分享一道不错的TS面试题(含3层),看看能答到第几层!的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/544347.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
《汤姆猫小镇》公测内容亮点
上一篇 2025年11月9日 16:43:21
大疆 Matrice 350 RTK 行业应用旗舰无人机发布,72428 元起
下一篇 2025年11月9日 16:43:28

相关推荐

  • VSCode语言服务器协议实现详解

    LSP通过客户端-服务器模型实现语言功能解耦,VSCode作为客户端与独立语言服务器通信,基于JSON-RPC协议交换消息,支持多编辑器复用、独立升级和稳定运行。 Visual Studio Code(简称 VSCode)之所以能支持数十种编程语言的智能提示、跳转定义、错误检查等功能,核心在于其采用…

    2026年9月10日
    100
  • Laravel路由是什么?路由如何定义使用?

    Laravel路由是应用的交通指挥中心,定义URL与代码的映射关系。它通过routes目录下的web.php和api.php文件,使用Route门面将HTTP动词与URL模式关联到控制器或闭包。路由支持参数、中间件、分组、资源路由等特性,贯穿请求生命周期,与中间件、服务容器协作完成请求处理。为管理复…

    2026年9月10日
    000
  • Laravel性能优化?优化技巧有哪些?

    Laravel性能优化需从数据库、缓存、前端、服务器和代码五方面系统提升。首先解决N+1查询与索引缺失,使用预加载with()和EXPLAIN分析执行计划;其次通过Redis缓存高频数据、配置、路由及视图,并合理设置过期时间;前端则压缩合并资源、优化图片并启用CDN;部署环境选用高版本PHP、开启O…

    2026年9月10日
    000
  • VSCode差异编辑器定制开发

    VSCode差异编辑器可通过扩展定制,利用API调用、装饰器高亮和AST分析实现语义级对比,提升代码审查效率。 Visual Studio Code(VSCode)的差异编辑器(Diff Editor)用于对比两个文件之间的差异,广泛应用于代码版本控制、合并冲突解决等场景。虽然 VSCode 提供了…

    2026年9月10日
    000
  • Laravel观察者模式?模型观察者如何使用?

    Laravel模型观察者用于解耦模型生命周期事件处理,通过创建观察者类、定义事件方法(如created、updating)并在AppServiceProvider中注册,实现对模型操作的响应。选择观察者适合处理与模型紧密相关的逻辑,而事件监听器更适合跨模块的解耦场景。saving在保存前执行,可修改…

    2026年9月10日
    000
  • 如何使用mysql开发在线考试系统

    答案:设计包含用户、科目、试题、试卷、答卷和成绩表的MySQL数据库,通过后端实现登录、组卷、考试、评分等功能,结合安全与性能优化措施,构建完整的在线考试系统。 开发一个在线考试系统,MySQL 是非常关键的后端数据支撑。它负责存储用户信息、试题内容、考试记录和成绩等核心数据。要使用 MySQL 开…

    2026年9月10日
    000
  • Laravel Echo用法?实时监听如何实现?

    Laravel Echo通过WebSocket实现客户端实时监听服务器事件,需安装laravel-echo及WebSocket服务(如Pusher或Laravel Websockets),配置.env和broadcasting.php,创建实现ShouldBroadcast的事件类并触发,前端引入E…

    2026年9月10日
    000
  • 在团队协作中,如何通过VSCode的设置同步扩展避免重复配置?

    通过配置文件统一VSCode开发环境,减少“在我机器上能运行”问题。1. 使用.extensions.json推荐扩展,新成员打开项目时自动提示安装;2. 在.settings.json中定义格式化工具、保存自动格式化等通用设置;3. 添加.editorconfig文件跨编辑器保持编码风格一致;4.…

    2026年9月10日
    000
  • vivo浏览器占内存大吗_vivo浏览器运行内存占用优化技巧

    vivo浏览器变慢或内存减少时,可通过清除缓存、管理标签页、限制后台活动、停用非必要插件、更新版本及使用i管家深度清理来优化。具体步骤:1. 进入浏览器设置清除缓存与数据;2. 关闭多余标签页并禁止后台运行;3. 管理扩展与脚本权限;4. 更新浏览器至最新版;5. 使用i管家进行一键优化和专项清理,…

    2026年9月10日
    000
  • Laravel事件广播?广播事件怎样配置?

    Laravel事件广播的核心优势在于实现服务器端事件的实时推送,提升用户体验。它通过WebSocket等技术将传统请求-响应模式转变为实时流式交互,确保客户端几乎即时接收更新,如聊天消息、订单状态等,避免轮询带来的延迟与资源浪费。该机制增强应用的响应性与互动性,降低服务器负载,同时通过频道授权(如私…

    2026年9月10日
    000
  • Linux怎么查看特定端口的流量统计

    使用iptables可精确统计特定端口的累计流量,通过添加INPUT和OUTPUT规则并查看其计数器实现;若需实时监控,则推荐iftop或nethogs;为确保规则重启后生效,需在Debian/Ubuntu上使用iptables-persistent,在RHEL/CentOS上保存至配置文件;此外,…

    2026年9月10日
    000
  • VSCode错误诊断报告系统

    诊断信息来自语言服务器协议(LSP)支持的扩展或内置服务,如TypeScript、Pylance等,通过分析代码实时提供错误、警告等反馈,并显示在问题面板和代码波浪线下。 VSCode 错误诊断报告系统是编辑器内置的一项功能,用于帮助开发者识别代码中的问题,比如语法错误、类型不匹配、未定义变量等。它…

    2026年9月10日
    000
  • VSCode插件:代码统计与分析工具

    答案:VSCode插件Lines可快速统计代码行数,支持按文件类型分类及过滤无关目录;Code Metrics分析函数复杂度与圈复杂度,助力代码优化;Project Statistic生成包含文件数量、语言占比等信息的可视化报告;WakaTime追踪编程时间并生成行为分析报告。根据需求选择使用,组合…

    2026年9月10日
    000
  • Laravel模型关联插入?关联数据如何添加?

    Laravel模型关联插入需根据关系类型选择方法:一对多可用create()、save()或createMany()批量插入;多对多通过attach()添加、sync()同步或syncWithoutDetaching()只增不减;反向关联可用associate()语义化绑定或直接赋值外键。 Lara…

    2026年9月10日
    100
  • Laravel广播系统?实时事件如何广播?

    答案是Laravel广播系统通过WebSocket实现实时事件推送,需配置广播驱动、定义广播事件并使用Laravel Echo在前端监听。首先选择Pusher或Redis等驱动,配置BROADCAST_DRIVER及对应密钥;创建实现ShouldBroadcast接口的事件类,通过broadcast…

    2026年9月10日
    100
  • 怎么在VSCode分段运行_VSCode分步执行代码教程

    分段运行代码在VSCode中主要通过调试器单步执行和交互式窗口分块运行实现。首先,使用调试器可设置断点并按F5启动,通过F10(单步跳过)、F11(单步进入)、Shift+F11(单步跳出)控制执行流程,同时在变量、监视、调用堆栈面板中观察程序状态,适用于定位复杂bug和理解深层逻辑;其次,利用Py…

    2026年9月10日
    100
  • VSCode扩展开发高级编程技巧

    掌握VSCode扩展开发需关注状态管理、事件释放、Webview安全与启动优化。使用globalState和workspaceState持久化数据并合理同步,避免频繁读写;通过context.subscriptions管理事件订阅防止内存泄漏,结合防抖提升性能;Webview中设置资源根目录、启用上…

    2026年9月10日
    200
  • VSCode代码:格式化与规范检查

    配置 Prettier 和 ESLint 可实现 VSCode 保存时自动格式化与规范检查。首先安装 Prettier 和 ESLint 扩展,创建 .prettierrc 定义格式规则,如分号、单引号、行宽等;在 settings.json 中启用 “editor.formatOnSa…

    2026年9月10日
    200
  • Laravel如何实现用户注册和登录_用户身份认证系统实现

    Laravel通过内置认证系统实现用户注册登录,使用Laravel UI或Jetstream生成基础功能,支持自定义逻辑、邮箱验证、第三方登录及路由保护。 直接说吧,Laravel 实现用户注册和登录,核心就在于它内置的身份认证系统。这套系统帮你处理了大部分底层逻辑,让你能更专注于业务层面。 注册和…

    2026年9月10日
    500
  • VSCode快速修复提供者开发

    实现快速修复需注册CodeActionProvider,监听诊断问题并返回CodeAction,支持TextEdit或WorkspaceEdit。 在 VSCode 中开发“快速修复”功能,本质是实现一个 代码操作(Code Action)提供者,用于响应用户在编辑器中遇到的问题并提供自动修复建议。…

    2026年9月10日
    100

发表回复

登录后才能评论
关注微信