手写Readonly与Pick引发的思考

2,477 阅读2分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

  • 想着锻炼一下自己 TS 的能力,就去找 antfu 老师的 TS 题目了,然后做的有些崩溃,想清楚之后来总结一下。

第一题

Pick

Question

实现 TS 内置的 Pick<T, K>,但不可以使用它。

从类型 T 中选择出属性 K,构造成一个新的类型


interface Todo {

title: string

description: string

completed: boolean

}

type TodoPreview = MyPick<Todo, 'title' | 'completed'>

const todo: TodoPreview = {

title: 'Clean room',

completed: false,

}



> View on GitHub: https://tsch.js.org/4




/* _____________ Your Code Here _____________ */


type MyPick<T, K> = any


/* _____________ Test Cases _____________ */

import type { Equal, Expect } from '@type-challenges/utils'


type cases = [

    Expect<Equal<Expected1, MyPick<Todo, 'title'>>>,

    Expect<Equal<Expected2, MyPick<Todo, 'title' | 'completed'>>>,

    // @ts-expect-error

    MyPick<Todo, 'title' | 'completed' | 'invalid'>,

]

interface Todo {

    title: string

    description: string

    completed: boolean

}


interface Expected1 {

    title: string

}


interface Expected2 {

    title: string

    completed: boolean

}

此处答案为

type MyPick<T, K extends keyof T> = {
    [key in K]: T[key]
}

就是说 K 只能扩展于 T 的 key ,然后对在 K 中的键值对应起来。

第二题

MyReadonly

不要使用内置的Readonly<T>,自己实现一个。

Readonly 会接收一个 泛型参数,并返回一个完全一样的类型,只是所有属性都会被 readonly 所修饰。

也就是不可以再对该对象的属性赋值。

/* _____________ 你的代码 _____________ */

type MyReadonly<T> = any

/* _____________ 测试用例 _____________ */

import type { Equal, Expect } from '@type-challenges/utils'

type cases = [

    Expect<Equal<MyReadonly<Todo1>, Readonly<Todo1>>>,

]


interface Todo1 {

    title: string

    description: string

    completed: boolean

    meta: {

        author: string

    }

}
  • 解答
type MyReadonly<T> = {
    readonly [key in keyof T]: T[key]
}
  • 其实之前做完这两道题的时候就满疑惑的,这都是啥,为啥一下 extends keyof ,一下 in ,又一下 in keyof

这里啊,我们还是要分而析之!

  1. extends

A extends B 的含义是,A 是 B 的子集

  1. keyof

A key of B 的含义是,A 是 B 的key的集合

  1. in

A in B 的含义是,对B中的每一个值进行遍历

  • 了解了这些东西之后,再来看这两个题目就比较好理解了。