携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
1. 引言
接着上一节中,接下来我们继续Ts中等篇的题型练习
https://github.com/type-challenges/type-challenges/blob/main/README.zh-CN.md 提供的TypeScript 类型体操姿势合集题型,目的是为了让大家更好的了解TS的类型系统,编写自己的类型工具,或者单纯的享受挑战的乐趣!
2. 题型
- RequiredByKeys:实现一个通用的RequiredByKeys<T, K>,它接收两个类型参数T和K。 K指定应设为必选的T的属性集。当没有提供K时,它就和普通的Required一样使所有的属性成为必选的。
interface User {
name?: string
age?: number
address?: string
}
type UserRequiredName = RequiredByKeys<User, 'name'> // { name: string; age?: number; address?: string }
思路:
首先确认下思路,除了传入的K以外的键变为必选属性时候其他键保持可选属性;那么这时候肯定需要用到Required(所有属性变为必填:其实Ts属性默认就是必填) 和 Merge(组合合并两个类型) 和 Omit(目标类型内除了要过滤的K键以外的其他对象类型属性) 三个方法;先拆分成三步:①:通过Omit获取到不需要改造的对象属性;②获取需要Required 改造的对象属性:需要先使用Exclude 获取到对象内目标键以外的其他键的联合类型集合;在使用Omit反向排除获得需要改造的对象元素集;在最后使用Required改造成 成员必填;③:使用Merge合并两个对象成一个对象;本题目完成;
注:PropertyKey = keyof T: 可以代指联合类型集合,可过滤unknown 等空类型
解答:
type Merge<T> = {
[P in keyof T]: T[P]
}
interface User {
name?: string
age?: number
address?: string
}
type RequiredByKeys<T,K extends PropertyKey = keyof T> = Merge<Omit<T,K> & Required< Omit<T,Exclude<keyof T,K>> >>
type Demo = RequiredByKeys<User, 'name'> // type Demo = {age?: number | undefined;address?: string | undefined;name: string;}
type Demo2 = RequiredByKeys<User, 'name' | 'unknown'> // type Demo2 = {age?: number | undefined;address?: string | undefined;name: string;}
type Demo3 = RequiredByKeys<User, 'name' | 'age'> // type Demo3 = {age: number | undefined;address?: string | undefined;name: string;}
type Demo4 = RequiredByKeys<User> // type Demo4 = {age: number | undefined;address: string | undefined;name: string;}
- Mutable:实现一个通用的类型 Mutable,使类型 T 的全部属性可变(非只读)。
interface Todo {
readonly title: string
readonly description: string
readonly completed: boolean
}
type MutableTodo = Mutable<Todo> // { title: string; description: string; completed: boolean; }
思路: 通过映射类型获取目标类型对象的键和键值;使用-readonly 去除对应对象元素的只读属性,完成。 解答:
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
}
type Demo1 = Mutable<Readonly<Todo1>> // {title: string;description: string;completed: boolean;meta: {author: string;};}
type Demo2 = Mutable<Readonly<List>> // type Demo2 = [1, 2, 3]
- OmitByType:从T中选择一组不能赋值给U的属性。
type OmitBoolean = OmitByType<{
name: string
count: number
isReadonly: boolean
isEnable: boolean
}, boolean> // { name: string; count: number }
思路: 首先理解题意,我们第一步需要做的就是从目标对象中的键值确认哪个是匹配我们需要过滤的U键值的;所以①:自定义一个PickTarget 为了获取匹配到对应U键值的对象元素;②: 通过Omit去除目标对象T中已经获取到匹配对应键值PickTarget 中的键;这样就获取到一个不含目标键值的类型对象了,完成。 解答:
type PickTarget<T, U> = {
[P in keyof T as U extends T[P] ? P : never]: T[P]
}
type OmitByType<T, U> = Omit<T,keyof PickTarget<T, U> >
type Demo = OmitByType<Model, boolean> // type Demo = {name: string;count: number;}
type Demo2 = OmitByType<Model, string> // type Demo2 = {count: number;isReadonly: boolean;isEnable: boolean;}
type Demo3 = OmitByType<Model, number> // type Demo3 = {name: string;isReadonly: boolean;isEnable: boolean;}