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

TypeScript 类型体操 03

原创
作者头像
一介程序员
发布2022-05-11 12:23:05
3720
发布2022-05-11 12:23:05
举报

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

00002-medium-return-type

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

type MyReturnType<T> = T extends (...params: any) => infer R ? R : never


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

type cases = [
  Expect<Equal<string, MyReturnType<() => string>>>,
  Expect<Equal<123, MyReturnType<() => 123>>>,
  Expect<Equal<ComplexObject, MyReturnType<() => ComplexObject>>>,
  Expect<Equal<Promise<boolean>, MyReturnType<() => Promise<boolean>>>>,
  Expect<Equal<() => 'foo', MyReturnType<() => () => 'foo'>>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn>>>,
  Expect<Equal<1 | 2, MyReturnType<typeof fn1>>>,
]

type ComplexObject = {
  a: [12, 'foo']
  bar: 'hello'
  prev(): number
}

const fn = (v: boolean) => v ? 1 : 2
const fn1 = (v: boolean, w: any) => v ? 1 : 2

00003-medium-omit

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

type MyOmit<T, K extends keyof T> = { [R in Exclude<keyof T, K>]: T[R] }


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

type cases = [
  Expect<Equal<Expected1, MyOmit<Todo, 'description'>>>,
  Expect<Equal<Expected2, MyOmit<Todo, 'description' | 'completed'>>>,
]

// @ts-expect-error
type error = MyOmit<Todo, 'description' | 'invalid'>

interface Todo {
  title: string
  description: string
  completed: boolean
}

interface Expected1 {
  title: string
  completed: boolean
}

interface Expected2 {
  title: string
}

需要说明的是,这里的MyOmit和TS中提供的Omit是不一样的,TS中的版本定义为:type Omit<T, K extends string | number | symbol> = { [P in Exclude<keyof T, K>]: T[P]; },不强制要求 Kkey of T中存在,但是这道题需要声明K extends keyof T 以满足type error = MyOmit<Todo, 'description' | 'invalid'>需要报错的测试用例

00008-medium-readonly-2

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

type MyReadonly2<T, K extends keyof T = keyof T> = { [R in Exclude<keyof T, K>]: T[R] } & { readonly [R in K]: T[R] }


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

type cases = [
  Expect<Alike<MyReadonly2<Todo1>, Readonly<Todo1>>>,
  Expect<Alike<MyReadonly2<Todo1, 'title' | 'description'>, Expected>>,
  Expect<Alike<MyReadonly2<Todo2, 'title' | 'description'>, Expected>>,
]

interface Todo1 {
  title: string
  description?: string
  completed: boolean
}

interface Todo2 {
  readonly title: string
  description?: string
  completed: boolean
}

interface Expected {
  readonly title: string
  readonly description?: string
  completed: boolean
}

这里最神奇的地方是:MyReadonly2<T, K extends keyof T = keyof T>中的K extends keyof T = keyof T他的意思是给K提供一个默认值,如果没有提供K,就使用默认值keyof T,真的是太神奇了。

00009-medium-deep-readonly

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

type DeepReadonly<T> = { readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]> }


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

type cases = [
  Expect<Equal<DeepReadonly<X>, Expected>>,
]

type X = {
  a: () => 22
  b: string
  c: {
    d: boolean
    e: {
      g: {
        h: {
          i: true
          j: 'string'
        }
        k: 'hello'
      }
      l: [
        'hi',
        {
          m: ['hey']
        },
      ]
    }
  }
}

type Expected = {
  readonly a: () => 22
  readonly b: string
  readonly c: {
    readonly d: boolean
    readonly e: {
      readonly g: {
        readonly h: {
          readonly i: true
          readonly j: 'string'
        }
        readonly k: 'hello'
      }
      readonly l: readonly [
        'hi',
        {
          readonly m: readonly ['hey']
        },
      ]
    }
  }
}

说实话这个地方我不太理解,有些很复杂的我觉得他是对的,但是太复杂了,也没搞懂。

00012-medium-chainable-options

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

type Chainable<T extends object = {}> = {
  option<K extends string, V extends unknown>(key: K extends keyof T ? never : K, value: V): Chainable<T & { [P in K]: V }>
  get(): T
}


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

declare const a: Chainable

const result1 = a
  .option('foo', 123)
  .option('bar', { value: 'Hello World' })
  .option('name', 'type-challenges')
  .get()

const result2 = a
  .option('name', 'another name')
  // @ts-expect-error
  .option('name', 'last name')
  .get()

type cases = [
  Expect<Alike<typeof result1, Expected1>>,
  Expect<Alike<typeof result2, Expected2>>,
]

type Expected1 = {
  foo: number
  bar: {
    value: string
  }
  name: string
}

type Expected2 = {
  name: string
}

话说这道题就离谱,还能这么搞的?简直大开眼界!有个知识点是:

image-20220511121437361.png
image-20220511121437361.png

大概意思是:如果T直接是一个Key, 就直接 {[p in T]: true},而不是:{[p in keyof T]: true}

00010-medium-tuple-to-union

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

type TupleToUnion<T extends any[]> = T[number]


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

type cases = [
  Expect<Equal<TupleToUnion<[123, '456', true]>, 123 | '456' | true>>,
  Expect<Equal<TupleToUnion<[123]>, 123>>,
]

00015-medium-last

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

type Last<T extends any[]> = T extends [...any[], infer res] ? res : never


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

type cases = [
  Expect<Equal<Last<[3, 2, 1]>, 1>>,
  Expect<Equal<Last<[() => 123, { a: string }]>, { a: string }>>,
]

00016-medium-pop

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

type Pop<T extends any[]> = T extends [... infer res, any] ? res : never


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

type cases = [
  Expect<Equal<Pop<[3, 2, 1]>, [3, 2]>>,
  Expect<Equal<Pop<['a', 'b', 'c', 'd']>, ['a', 'b', 'c']>>,
]

00015、00016是相似的,不过00016的写法我倒是之前没有想到会这样的。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 00002-medium-return-type
  • 00003-medium-omit
  • 00008-medium-readonly-2
  • 00009-medium-deep-readonly
  • 00012-medium-chainable-options
  • 00010-medium-tuple-to-union
  • 00015-medium-last
  • 00016-medium-pop
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档