TS-实现 Pick
TS-实现 Pick
实现 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,
}
一、关键词说明
type :类型保护
type
作用就是给类型起一个新名字(别名),支持基本类型、联合类型、元祖及其它任何你需要的手写类型,常用于联合类型,与接口一样,用来描述对象或函数的类型
typeof
就是js中的typeof
, ts会根据你代码中出现的typeof
来自动推断类型:
// 通常type是这样定义类型的
type PersonType = {
name: string
age: number
}
let people = {
name: 'people',
age: 20,
}
//typeof 可以由一个普通对象来定义一个类型A出来,然后用这个类型A去约束其他的变量
type PersonType1 = typeof people
let p1: PersonType = {
name: 'man',
age: 12,
}
pick
Pick
在 TS 中的源码实现:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
它的作用是从 T 中将所有的 K提取出来,并生成一个新的类型。下面示例中定义的User
类型包含了两个必选属性 stature 和 age
。所以,将people
指定为User
类型之后,就肯定必须包含这两个属性,否则就会报类型检查错误:
interface User {
stature: number
age: number
}
const people: User = {
stature: 186,
age: 20,
}
但是,如果我想让people
只包含User
类型的 stature
属性,就可以用Pick
这样来实现。它就是告诉 TS 仅仅将 stature
属性从User
中提取出来即可。
// success,使用 Pick 生成的新类型确实只包含 stature 属性
const people: Pick<User, 'stature'> = {
stature: 185,
}
// error,使用 Pick 生成的新类型中并不包含 age 属性
const people: Pick<User, 'stature'> = {
age: 17,
}
PS :Pick
是将类型中的所有的属性中某一个属性单独提取出来。
interface
它相当于类型中的 JS
对象,用于对函数、类等进行结构类型检查,所谓的结构类型检查,就是两个类型的结构一样,那么它们的类型就是兼容的,这在计算机科学的世界里也被成为 “鸭子类型”。
提示 什么鸭子类型? 当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
现在我们要这个 todo
对象 做一个类型注解,根据之前提到的 “鸭子类型” 的方式,我们可以定义一个 Interface 来为它做注解:
interface Todo {
content: string;
user: string;
time: string;
isCompleted: boolean;
}
const todo: Todo = {
// ...
}
type生成interface
interface IPerson {
name: string
age: number
like: Array<string>
}
interface IPickedPerson extends Pick<IPerson, 'name' | 'like'> { } //生成新的接口
const p: IPickedPerson = { name: 'zs', like: ['a', 'b'] }
二、pick 使用方式
场景:从一个复合类型中,取出几个想要的类型的组合。
// 原始类型
interface TState {
name: string;
age: number;
like: string[];
}
// 如果只想要name和age最直接就是再定义一个
interface TSingleState {
name: string;
age: number;
}
// 这样的弊是在Tstate发生改变的时候,TSingleState并不会跟着一起改变,所以应该使用Pick
interface TSingleState extends Pick<TState, "name" | "age"> {};
一般来说我们使用extends
是用来继承的,但是写在泛型里的作用又是什么呢? 泛型约束官方文档
之前的一个例子,我们有时候想操作某类型的一组值,并且我们知道这组值具有什么样的属性。 在 demo
例子中,我们想访问value
的length
属性,但是编译器并不能证明每种类型都有length
属性,所以就报错了。
function demo<T>(value: T): T {
console.log(value.length); // Error: T doesn't have .length
return value;
}
相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。
为此,我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束:
interface Lengthwise {
length: number;
}
function demo<T extends Lengthwise>(value: T): T {
console.log(value.length); // Now we know it has a .length property, so no more error
return value;
}
//现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
demo(3); // Error, number doesn't have a .length property
//需要传入符合约束类型的值,必须包含必须的属性:
demo({length: 10, value: 3});
三、题解
interface Todo {
title: string
description: string
completed: boolean
}
// 从 T 中取出 一系列 K 的属性
type MyPick<T, K extends keyof T> = { [S in K]: T[S] }
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
在泛型中使用extends并不是用来继承的,而是用来约束类型的。所以这个K extends keyof T
,应该是说key被约束在T的key中,不能超出这个范围,否则会报错的。
转载自:https://juejin.cn/post/7005376193738309663