德胜云资讯,添加一些关于程序相关的内容,仅供大家学习交流(https://www.wxclwl.com)

网站地图

搜索
德胜云咨询
前端分类 javascript CSS 正则表达式 html 前端框架 typescript Ajax
热门标签:
最新标签:

typescript多久能学会想去力扣当前端,TypeScript 需要掌握到什么程度?typescript题目万万没想到,

日期:2023/03/25 18:53作者:王雅娥人气:

导读:2018 年底的时候,力扣发布了岗位招聘,其中就有前端。与大多数 JD 不同, 其提供了 5 道题, 并注明了: 完成一个或多个面试题,获取免第...

作者:一个脑洞很大的程序员

转发链接:

https://mp.weixin.qq.com/s/3dP9jxkAEBqFEN7D4jrIVA

前言

2018 年底的时候,力扣发布了岗位招聘,其中就有前端,仓库地址:

https://github.com/LeetCode-OpenSource/hire 。与大多数 JD 不同, 其提供了 5 道题, 并注明了: 完成一个或多个面试题,获取免第一轮面试的面试机会。完成的题目越多,质量越高,在面试中的加分更多。完成后的代码可以任意形式发送给 jobs@lingkou.com。以上几个问题完成一个或多个都有可能获得面试机会,具体情况取决于提交给我们的代码。

(力扣中国前端工程师 JD)

今天我们就来看下第二题:编写复杂的 TypeScript 类型。通过这道题来看下, TypeScript 究竟要到什么水平才能进力扣当前端?

其它四道题也蛮有意思的,值得一看。

问题描述

假设有一个叫 EffectModule 的类

class EffectModule {}

这个对象上的方法「只可能」有两种类型签名:

interface Action<T> {   payload?: T   typestring } asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>> syncMethod<T, U>(action: Action<T>): Action<U>

这个对象上还可能有一些任意的「非函数属性」

interface Action<T> {   payload?: T;   typestring; } class EffectModule {   count = 1;   message = "hello!";   delay(input: Promise<number>) {     return input.then((i) => ({       payload: `hello ${i}!`,       type"delay",     }));   }   setMessage(action: Action<Date>) {     return {       payload: action.payload!.getMilliseconds(),       type"set-message",     };   } }

现在有一个叫 connect 的函数,它接受 EffectModule 实例,将它变成另一个对象,这个对象上只有「EffectModule 的同名方法」,但是方法的类型签名被改变了:

asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>  变成了 asyncMethod<T, U>(input: T): Action<U>syncMethod<T, U>(action: Action<T>): Action<U>  变成了 syncMethod<T, U>(action: T): Action<U>

例子:

EffectModule 定义如下:

interface Action<T> {   payload?: T;   typestring; } class EffectModule {   count = 1;   message = "hello!";   delay(input: Promise<number>) {     return input.then((i) => ({       payload: `hello ${i}!`,       type"delay",     }));   }   setMessage(action: Action<Date>) {     return {       payload: action.payload!.getMilliseconds(),       type"set-message",     };   } }

connect 之后:

type Connected = {   delay(input: number): Action<string>;   setMessage(action: Date): Action<number>; }; const effectModule = new EffectModule(); const connected: Connected = connect(effectModule);

要求:

在 题目链接[1] 里面的 index.ts 文件中,有一个 type Connect = (module: EffectModule) => any,将 any 替换成题目的解答,让编译能够顺利通过,并且 index.ts 中 connected 的类型如:

type Connected = {   delay(input: number): Action<string>;   setMessage(action: Date): Action<number>; };

「完全匹配」

以上是官方题目描述,下面我的补充

上文提到的index.ts 比 题目描述多了两个语句,它们分别是:

(题目额外信息)

思路

首先来解读下题目。题目要求我们补充类型 Connect 的定义, 也就是将 any 替换为不报错的其他代码。

回顾一下题目信息:

有一个叫 connect 的函数,它接受 EffectModule 实例,将它变成另一个对象,这个对象上只有「EffectModule 的同名方法」,但是方法的类型签名被改变了这个对象上还可能有一些任意的「非函数属性」这个对象(EffectModule 实例)上的方法「只可能」有两种类型签名

根据以上信息,我们能够得到:我们只需要将作为参数传递进来的 EffectModule 实例上的函数类型签名修改一下,非函数属性去掉即可。所以,我们有两件问题要解决:

如何将非函数属性去掉如何转换函数类型签名

如何将非函数属性去掉

我们需要定义一个泛型,功能是接受一个对象,如果对象的 value 是 函数,则保留,否则去掉即可。不懂泛型的朋友可以先看下我之前写的文章:你不知道的 TypeScript 泛型(万字长文,建议收藏)[2]

这让我想起了官方提供的 Omit 泛型 Omit<T,K>。举个例子:

interface Todo {   title: string;   description: string;   completed: boolean; } type TodoPreview = Omit<Todo, "description">; // description 属性没了 const todo: TodoPreview = {   title: "Clean room",   completed: false, };

官方的 Omit 实现:

type Pick<T, K extends keyof T> = {   [P in K]: T[P]; }; type Exclude<T, U> = T extends U ? never : T; type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

实际上我们要做的就是 Omit 的变种,不是 Omit 某些 key,而是 Omit 值为非函数的 key。

由于 Omit 非函数实际就就是 Pick 函数,并且无需显式指定 key,因此我们的泛型只接受一个参数即可。于是模仿官方的 Pick 写出了如下代码:

// 获取值为函数的 key,形如: funcKeyA | funcKeyB type PickFuncKeys<T> = {   [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]; // 获取值为函数的 key value 对,形如: { funcKeyA: ..., funKeyB: ...} type PickFunc<T> = Pick<T, PickFuncKeys<T>>;

使用效果:

interface Todo {   title: string;   description: string;   addTodo(): string; } type AddTodo = PickFunc<Todo>; const todo: AddTodo = {   addTodo() {     return "关注脑洞前端~";   }, }; type ADDTodoKey = PickFuncKeys<Todo>; // addTodo

可以看出,PickFunc 只提取了函数属性,忽略了非函数属性。

如何转换函数类型签名

我们再来回顾一下题目要求:

也就是我们需要知道「怎么才能提取 Promise 和 Action 泛型中的值」

实际上这两个几乎一样,会了一个,另外一个也就会了。我们先来看下 Promise。

从:

(arg: Promise<T>) => Promise<U>

变为:

(arg: T) => U;

如果想要完成这个需求,需要借助infer。只需要在类型前加一个关键字前缀 infer,TS 会将推导出的类型自动填充进去。

infer 最早出现在此 官方 PR 中,表示在 extends 条件语句中待推断的类型变量。

简单示例如下:

type ParamType<T> = T extends (param: infer P) => any ? P : T;

在这个条件语句 T extends (param: infer P) => any ? P : T中,infer P 表示待推断的函数参数。

整句表示为:如果 T 能赋值给 (param: infer P) => any,则结果是 (param: infer P) => any 类型中的参数 P,否则返回为 T。

一个更具体的例子:

interface User {   name: string;   age: number; } type Func = (user: User) => void; type Param = ParamType<Func>; // Param = User type AA = ParamType<string>; // string

这些知识已经够我们用了。更多用法可以参考 深入理解 TypeScript - infer[3] 。

根据上面的知识,不难写出如下代码:

type ExtractPromise<P> = {   [K in PickFuncKeys<P>]: P[K] extends (     arg: Promise<infer T>   ) => Promise<infer U>     ? (arg: T) => U     : never; };

提取 Action 的 代码也是类似:

type ExtractAction<P> = {   [K in keyof PickFunc<P>]: P[K] extends (     arg: Action<infer T>   ) => Action<infer U>     ? (arg: T) => Action<U>     : never; };

至此我们已经解决了全部两个问题,完整代码见下方代码区。

关键点

泛型extends 做类型约束infer 做类型提取内置基本范型的使用和实现

代码

我们将这几个点串起来,不难写出如下最终代码:

type ExtractContainer<P> =  {   [K in PickFuncKeys<P>]:     P[K] extends (arg: Promise<infer T>) => Promise<infer U> ? (arg: T) => U :       P[K] extends (arg: Action<infer T>) => Action<infer U> ? (arg: T) => Action<U> :         never type Connect = (module: EffectModule) => ExtractContainer<EffectModule>

完整代码在我的 Gist[4] 上。

总结

我们先对问题进行定义,然后分解问题为:

1. 如何将非函数属性去掉,

2. 如何转换函数类型签名。最后从分解的问题,以及基础泛型工具入手,联系到可能用到的语法。

这个题目不算难,最多只是中等。但是你可能也看出来了,其不仅仅是考一个语法和 API 而已,而是考综合实力。这点在其他四道题体现地尤为明显。这种考察方式能真正考察一个人的综合实力,背题是背不来的。我个人在面试别人的时候也非常喜欢问这种问题。

只有「掌握基础 + 解决问题的思维方法」,面对复杂问题才能从容不迫,手到擒来。

推荐TypeScript知识点文章

TypeScript 4.0 Beta 版本正式发布

「干货」将数十万行CoffeeScript代码迁移到TypeScript

TypeScript中的类型断言详解

深入浅出TypeScript在Model中的高级应用

让人眼前一亮的 10 大 TypeScript 项目

拿6个案例讲解TypeScript 知识点「干货」

TypeScript 中的顶级类型:any 和 unknown

1500行TypeScript代码在React中实现组件keep-alive

「TypeScript」详解一个了不起的 tsconfig.json 指南

用TypeScript编写React的优雅实践「干货」

了不起的 TypeScript 入门教程「实践篇」

了不起的 TypeScript 入门教程「基础篇」

TypeScript 常见问题整理(60多个)「上」

TypeScript 常见问题整理(60多个)「下」

深入TypeScript难点梳理讲解

Vue3.0之前你必须知道的TypeScript实战技巧

深入TypeScript难点梳理讲解

Vue3.0之前你必须知道的TypeScript实战技巧

你需要的 React + TypeScript 50 条规范和经验

TypeScript详细概括【思维导图】

Vue3.0 尝鲜 Hook TypeScript 取代 Vuex【项目实践】

TypeScript详细概括【思维导图】

「新消息」基于JavaScript/TypeScript 编程环境Deno1.0 即将发布

「干货」一张页面引起的项目架构思考(rax+Typescript+hooks)

深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】

作者:一个脑洞很大的程序员

转发链接:

https://mp.weixin.qq.com/s/3dP9jxkAEBqFEN7D4jrIVA

排行

网站地图

Copyright © 2002-2022 香港德胜云网络 版权所有 | 备案号:蜀ICP备2023007363号-5

声明: 本站内容全部来自互联网,非盈利性网站仅供学习交流