TypeScript 类型体操——实现Pick

119 阅读1分钟

前言

简单题:实现Pick
TS Playground:github.com/type-challe…

正文

题目

4 - Pick
-------
by Anthony Fu (@antfu) #easy #union #built-in

### Question

Implement the built-in `Pick<T, K>` generic without using it.

Constructs a type by picking the set of properties `K` from `T`

For example:

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]
};

总结

keyof:取interface的键保存为联合类型

interface user {
    name: string,
    age: number
}
type keyofUser = keyof user;
// keyofUser = 'name' | 'age'

in:取联合类型的值,主要用于数组和对象的构建

type name = 'firstname' | 'lastname';
type TName = {
    [key in name]: string
};
// TName = { firstname: string, lastname: string };

实际运用举例:

function getVal(o: object, k: string) {
    return o[k];
}
const user = { name: '张三', age: 18 };
const nameVal = getVal(user, 'name'); // 张三

以上写法的缺点:

  1. 无法确定返回值类型
  2. 无法对 key 进行约束
function getVal<T extends object, K extends keyof T>(o: T, k: K): T[K] {
    return o[k];
}
const user = { name: '张三', age: 18 };
const nameVal = getVal(user, 'name'); // 张三

此时,如果第二个参数 k 不是 user 的 key 值(name/age)就会报错