前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TypeScript 类型体操 02

TypeScript 类型体操 02

原创
作者头像
一介程序员
发布2022-05-04 07:13:10
4380
发布2022-05-04 07:13:10
举报
文章被收录于专栏:写两行代码放松下

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

00043-easy-exclude

代码语言:typescript
复制
/* _____________ 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
复制
/* _____________ 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
复制
/* _____________ 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
复制
/* _____________ 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
复制
/* _____________ 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
复制
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
复制
/* _____________ 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
复制
/* _____________ 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
复制
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 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 00043-easy-exclude
  • 00189-easy-awaited
  • 00268-easy-if
  • 00533-easy-concat
  • 00898-easy-includes
  • 03057-easy-push
  • 03060-easy-unshift
  • 03312-easy-parameters
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档