首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >TypeScript 类型体操 02

TypeScript 类型体操 02

原创
作者头像
发布于 2022-05-03 23:13:10
发布于 2022-05-03 23:13:10
4760
举报

发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。

00043-easy-exclude

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type MyExclude<T, U> = T extends U ? never : T


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, Exclude<'a' | 'b' | 'c', 'a'>>>,
  Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, Exclude<'a' | 'b' | 'c', 'a' | 'b'>>>,
  Expect<Equal<MyExclude<string | number | (() => void), Function>, Exclude<string | number | (() => void), Function>>>,
]

这里最神奇的是extends的用法,可以理解为,对于 T extends U,如果T是一个单一类型,那么就判断T是不是extends U,如果T是一个复合类型,就依次判断T中每一个元素是不是extends U

00189-easy-awaited

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type MyAwaitedR<T> = T extends Promise<infer K> ? MyAwaitedR<K> : T
type MyAwaited<T> = T extends Promise<infer K> ? MyAwaitedR<K> : error


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>

type cases = [
  Expect<Equal<MyAwaited<X>, string>>,
  Expect<Equal<MyAwaited<Y>, { field: number }>>,
  Expect<Equal<MyAwaited<Z>, string | number>>,
]

// @ts-expect-error
type error = MyAwaited<number>

这里的关键点是:

  1. 有很多人答案是:type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T,但是这样不满足最后一个error的判断条件,因为MyAwaited<number> 返回的是 number不是error
  2. 我一开始写成:type MyAwaited<T> = T extends Promise<infer R> ? R extends Promise<infer M> ? M : R : error这样确实可以满足题目中所有的情况,但是如果把type Z = Promise<Promise<string | number>>改成type Z = Promise<Promise<Promise<string | number>>>就不行了,所以还是得用递归来实现。(这里无限套娃Promise应该是可以的吧?)

00268-easy-if

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type If<C, T, F> = C extends true ? T : C extends false ? F : error


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<If<true, 'a', 'b'>, 'a'>>,
  Expect<Equal<If<false, 'a', 2>, 2>>,
]

// @ts-expect-error
type error = If<null, 'a', 'b'>

00533-easy-concat

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U]


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Concat<[], []>, []>>,
  Expect<Equal<Concat<[], [1]>, [1]>>,
  Expect<Equal<Concat<[1, 2], [3, 4]>, [1, 2, 3, 4]>>,
  Expect<Equal<Concat<['1', 2, '3'], [false, boolean, '4']>, ['1', 2, '3', false, boolean, '4']>>,
]

00898-easy-includes

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type Includes<T extends readonly any[], U> = T extends [infer X, ...infer Y] ? (Equal<X, U> extends true ? true: Includes<Y,U>) : false


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
  Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
  Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
  Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
  Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
  Expect<Equal<Includes<[{}], { a: 'A' }>, false>>,
  Expect<Equal<Includes<[boolean, 2, 3, 5, 6, 7], false>, false>>,
  Expect<Equal<Includes<[true, 2, 3, 5, 6, 7], boolean>, false>>,
  Expect<Equal<Includes<[false, 2, 3, 5, 6, 7], false>, true>>,
  Expect<Equal<Includes<[{ a: 'A' }], { readonly a: 'A' }>, false>>,
  Expect<Equal<Includes<[{ readonly a: 'A' }], { a: 'A' }>, false>>,
  Expect<Equal<Includes<[1], 1 | 2>, false>>,
  Expect<Equal<Includes<[1 | 2], 1>, false>>,
  Expect<Equal<Includes<[null], undefined>, false>>,
  Expect<Equal<Includes<[undefined], null>, false>>,
]

迷一样的898,我真搞不懂,这道题怎么会被分到简单里面的?答案参考的这里

但是我觉得这个方法也不是很完美,因为Equal这个东西是"@type-challenges/utils"里面的东西,不是TS自带的。

一开始,我是就是很简单得这么实现的:

image-20220503203538632.png
image-20220503203538632.png

后来参考了大佬的答案,我想实现一个自己的Equal函数,结果有两条过不去,经过测试,面对readonly属性有点无能为力

image-20220503203651363.png
image-20220503203651363.png

单独研究一下这个Equal是怎么实现的:

代码语言:typescript
AI代码解释
复制
export type Equal<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false

说实话也没怎么看懂,网上有个专门讨论这个问题的帖子

但是这个Equal也不完美,他有个问题如下:

image-20220503234407462.png
image-20220503234407462.png

理论上 { a: string } & { b: string }{ a: string; b: string }这两个应该是相等的,但是这个Equal居然判断不相等。

这道题就先过吧。

03057-easy-push

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type Push<T extends any[], U> = [...T, U]


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
import { ExpectFalse, NotEqual } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Push<[], 1>, [1]>>,
  Expect<Equal<Push<[1, 2], '3'>, [1, 2, '3']>>,
  Expect<Equal<Push<['1', 2, '3'], boolean>, ['1', 2, '3', boolean]>>,
]

03060-easy-unshift

代码语言:typescript
AI代码解释
复制
/* _____________ Your Code Here _____________ */

type Unshift<T extends any[], U> = [U, ...T]


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'
import { ExpectFalse, NotEqual } from '@type-challenges/utils'

type cases = [
  Expect<Equal<Unshift<[], 1>, [1]>>,
  Expect<Equal<Unshift<[1, 2], 0>, [0, 1, 2 ]>>,
  Expect<Equal<Unshift<['1', 2, '3'], boolean>, [boolean, '1', 2, '3']>>,
]

03312-easy-parameters

代码语言:typescript
AI代码解释
复制
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer R) => any ? R : never


/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {}
const baz = (): void => {}

type cases = [
  Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
  Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
  Expect<Equal<MyParameters<typeof baz>, []>>,
]

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【类型挑战】Unshift,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 条件类型的灵活运用; 分布式条件类型的灵活运用; 解构完美解答此题; 题目分析: 题目地址:3060-easy-unshift 这道题的题面应该也不用多数,实现一个类数组unshift的功能,将我们的U传入到T的第一个位置。 题目解答: 测试用例: 测试用例和上一道题几乎是一样的,,只是向数组第一个元素的位置插入新的元素,我们这次的解法是不是可以直接复用呢?一起来看一下。 /* ____
前端小鑫同学
2022/12/26
3470
【类型挑战】Unshift,难度⭐️
【类型挑战】Parameters,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 认识什么parameterstype和其作用;题目分析: 题目地址:3312-easy-parameters 的作用就是获取一个函数的参数类型组成的元祖,接下来我们通过这道题来详细看一下。 题目解答: 测试用例: 本次的测试案例可以看出,我们需要考虑输入类型为内置类型,字面量类型和未传入类型的情况,测试用例同样使用到了typeof来讲js世界申明的变量转换为类型世界的类型。 /* _
前端小鑫同学
2022/12/26
3790
【类型挑战】Parameters,难度⭐️
【类型挑战】第一个元素,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 条件类型的使用:文档地址 infer关键字也参照条件类型使用的文档 在泛型中对类型做约束:文档地址 索引访问类型:文档地址 题目分析: 题目地址:14-easy-first 如上图所示我们需要设计一个通用的类型转换工具来提取一组类型中的第一个元素类型,类似于我们数组直接使用0号下标获取第一个元素。 题目解答: 测试用例: 通过测试用例可以看到仅当我们传入的是一个数组或元祖时才可以返回第
前端小鑫同学
2022/12/26
3410
【类型挑战】第一个元素,难度⭐️
【类型挑战】实现 Pick,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 在实现Pick这道题目的过程中运用到的知识点如下: Keyof 类型运算符; Mapped Types 映射类型; Indexed Access Types:索引访问类型; 泛型中约束类型参数; 题目分析: 题目地址:4-easy-pick 如图所示我们需要设计一个通用类型工具MyPick来支持从接口Todo中获取到title或completed属性并组成一个新的类型。
前端小鑫同学
2022/12/26
3420
【类型挑战】实现 Pick,难度⭐️
TypeScript 类型体操 03
发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。
2022/05/11
4350
TypeScript 类型体操 01
发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。
2022/05/03
4460
TypeScript 类型体操 04
发现一个好玩的开源项目:type-challenges,在上面可以做一些TypeScript类型相关的题目,这里记录一下自己的学习。
2022/05/13
4440
TypeScript 体操,从进阶到放弃!
最近有了面试的打算,所以抽空整理了一些高难度的内容,有兴趣的小伙伴可以跟我一起慢慢过一遍,一定要要自己手写一遍,不会了在来这里参考!
萌萌哒草头将军
2025/04/30
1870
TypeScript 体操,从进阶到放弃!
【类型挑战】Awaited,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 在条件类型中进行类型推断 泛型约束 题目分析: 题目地址:189-easy-awaited 我们在使用Promise时往往可以通过泛型来约束最后返回时的对象类型,我们这道题就需要设计一个类型工具来得到Promise返回的这个类型。 题目解答: 测试用例: 约束传入的类型一定是一个Promise对象,当传入非Promise对象时将抛出错误。 当我们传入Promise对象时将返回对应T的类
前端小鑫同学
2022/12/26
4660
【类型挑战】Awaited,难度⭐️
【类型挑战】Includes,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 条件类型的使用与学习; 索引访问类型的使用与学习; 数组解构的应用; 递归的应用;题目分析: 题目地址:898-easy-includes 如上图所示我们需要实现一个includes函数,当我们传入的类型在所给数组中存在在返回true,反之返回false,那么我们就在类型编程实现这么一个通用的类型工具吧。题目解答: 测试用例: 本次的测试用例很多,这道看似简单的问题实际上并不简
前端小鑫同学
2022/12/26
4450
【类型挑战】Includes,难度⭐️
【类型挑战】Concat,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 泛型约束输入类型; 数组解构参照ES6文档。 题目分析: 题目地址:533-easy-concat 本次案例相对简单,通过实现一个通用的类型工具支持将两个数组中包含的类型合并到一个数组集合中。功能类似于我们在JavaScript中的Array.concat函数。 题目解答: 测试用例: 依次将两个数组中的元素提取并按顺序进行整合。 当两个数组传入均为空的时候,我们得
前端小鑫同学
2022/12/26
1.1K0
【类型挑战】Concat,难度⭐️
【类型挑战】实现 Omit,难度⭐️⭐️
如上图所示,我们需要实现的通用类型工具的要求需要满足可以将传入的对象在忽略掉指定key的字段后再返回输出。
前端小鑫同学
2022/12/26
3340
【类型挑战】实现 Omit,难度⭐️⭐️
【类型挑战】If,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 要求限制输入为指定类型时需要考虑到使用泛型进行约束; 当需要进行类型选择判断的使用考虑使用条件类型; 题目分析: 题目地址:268-easy-if 如上图所示我们需要实现一个通用的类型工具,来实现if条件判断,当传入的第一个参数的结果为真则返回第二个参数的类型,反之返回第三个参数的类型 题目解答: 测试用例: 当输入第一个参数为真,则返回第二个参数类型‘a’; 当输入第
前端小鑫同学
2022/12/26
3950
【类型挑战】If,难度⭐️
【类型挑战】元组转换为对象,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 认识什么是as const,有什么作用; 如何得到数组类型中的元素类型; Typeof 类型运算符的到被引用变量或属性的类型; 题目分析: 题目地址:11-easy-tuple-to-object 如上图所示我们需要设计的类型工具需要满足将一个数组转为由其元素组成的key和value看着相同的对象类型。题目解答: 测试用例: 测试用例还是挺简单的比较经过我们设计的类型工具处理后的
前端小鑫同学
2022/12/26
6990
【类型挑战】元组转换为对象,难度⭐️
【类型挑战】获取元祖长度,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 目前还用的少的类型unknown,可以看一下抽象类型的分配性。 还有必不可少的泛型约束。 通过索引形式获取数组类型长度。 题目分析: 题目地址:18-easy-tuple-length 如上图所示我们需要设计一个通用的获取数组长度的类型工具,当我们传入一个只读数组后返回它的实际长度。 题目解答: 测试用例: 测试用例要求tesla的长度符合预期4和spaceX的长度符合
前端小鑫同学
2022/12/26
2.1K0
【类型挑战】获取元祖长度,难度⭐️
【类型挑战】Exclude,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: Exclude将从联合类型中排除特点的类型后输出剩余联合类型 条件类型运用 分布式条件类型运用题目分析: 题目地址:43-easy-exclude 如上图所示我们需要设计一个与内置Exclude功能一致的类型工具,从已知的联合类型中排除指定类型后将剩余类型返回。 题目解答: 测试用例: /* _____________ 测试用例 _____________ */ import {
前端小鑫同学
2022/12/26
6250
【类型挑战】Exclude,难度⭐️
精读《type challenges - easy》
TS 强类型非常好用,但在实际运用中,免不了遇到一些难以描述,反复看官方文档也解决不了的问题,至今为止也没有任何一篇文档,或者一套教材可以解决所有犄角旮旯的类型问题。为什么会这样呢?因为 TS 并不是简单的注释器,而是一门图灵完备的语言,所以很多问题的解决方法藏在基础能力里,但你学会了基础能力又不一定能想到这么用。
黄子毅
2022/06/10
7340
【类型挑战】实现 Readonly,难度⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 认识什么是readonly,和常量的区别是什么? Indexed Access Types:索引访问类型; Keyof 类型运算符; Mapped Types 映射类型; 题目分析: 题目地址:7-easy-readonly 如图所示我们需要设计一个通用类型工具MyReadonly接收传入的Todo接口并遍历每一条属性去设置为只能读取不能编辑,并返回这样的类型结构,功能
前端小鑫同学
2022/12/26
5630
【类型挑战】实现 Readonly,难度⭐️
【类型挑战】最后一个元素,难度⭐️⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 条件类型的使用:文档地址 infer关键字也参照条件类型使用的文档 在泛型中对类型做约束:文档地址 索引访问类型:文档地址 题目分析: 题目地址:15-medium-last 如上图所示,我们需要设计的通用类型工具需要满足重一个数组中取出最后一个元素来当我们的输出类型。本题和我们一开始取数组类型第一个元素一样。 题目解答: 测试用例: 需满足两个测试用例,分别将使用Last类型工具的到
前端小鑫同学
2022/12/26
1.1K0
【类型挑战】最后一个元素,难度⭐️⭐️
【类型挑战】深度 Readonly,难度⭐️⭐️
Dear,大家好,我是“前端小鑫同学”,😇长期从事前端开发,安卓开发,热衷技术,在编程路上越走越远~ 知识运用: 基础对象Readonly处理。 递归在类型编程中的运用。 如果类型的内容是函数类型,那么keyof得到的事never类型。题目分析: 题目地址:9-medium-deep-readonly 如上图所示,我们需要设计一个通用的类型工具可以将一个深度嵌套的对象的key全部处理成readonly。 题目解答: 测试用例: 测试用例比较简单,我们来看答案解析吧。 /* ____________
前端小鑫同学
2022/12/26
7000
【类型挑战】深度 Readonly,难度⭐️⭐️
相关推荐
【类型挑战】Unshift,难度⭐️
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档