持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
中等
PickByType
type ExcludeKey<T, U, K extends keyof T = keyof T> = K extends K ? T[K] extends U ? K : never : never;
type PickByType<T, U> = {
[P in ExcludeKey<T, U>]: T[P];
}
- 我这里的是在使用
in遍历T之前就将其中键进行过滤,主要发挥作用的是ExcludeKey
通过具体示例来看看ExcludeKey的作用:
// 假如有:
interface Model {
name: string
count: number
isReadonly: boolean
isEnable: boolean
}
type T = PickByType<Model, boolean>;
// 那么直接代入到 ExcludeKey 中则有:
// 此时 K = name | count | isReadonly | isEnable
type ExcludeKey<T, U, K extends keyof T = keyof T> = K extends K ? T[K] extends U ? K : never : never;
K extends K会产生分布,这样对每个键进行处理,那么最后获取的类型就是我们需要的键了。
我的答案略显复杂了点,在解答区看到一个更加简洁的:
type PickByType<T, U> = { [K in keyof T as T[K] extends U ? K : never]: T[K] }
在TS4.1的版本以上,可以通过 as 来创建一个新的键类型,所以就能直接进行过滤了。
StartsWith
type StartsWith<T extends string, U extends string> = T extends `${U}${string}` ? true : false;
- 这个比较简单,通过字符串模板进行条件类型判断即可
EndsWith
type EndsWith<T extends string, U extends string> = T extends `${string}${U}` ? true : false;
- 这个跟
StartsWith比,就是反过来了而已,所以还是一样的套路...
PartialByKeys
type PartialByKeys<T extends {}, U = keyof T> =
Omit<Partial<Pick<T, U & keyof T>> & Omit<T, U & keyof T>, never>;
参考自解答区👍最多的
先通过一个具体的case来进行分析:
// case 如下:
interface User {
name: string
age: number
address: string
}
interface UserPartialName {
name?: string
age: number
address: string
}
Expect<Equal<PartialByKeys<User, 'name'>, UserPartialName>>
- 代入到类型中:
// 此时 T 为 User,U 为 'name'
type PartialByKeys = Omit<Partial<Pick<T, U & keyof T>> & Omit<T, U & keyof T>, never>;
- 先计算内部
&右边的Omit:
Omit<User, 'name' & keyof User>
// 相当于:(注意(),这是一个整体)
Omit<User, 'name' & ('name' | 'age' | 'address')>;
// & 是交叉类型运算符,返回的是交集,运算后结果为
Omit<User, 'name'>;
// 最终结果:
{
age: number
address: string
}
上面代码中,'name' & keyof User得到'name',因为它们的交集就是'name',如果没有交集,则会返回never,例如:
// T 的类型为 never
type T = 'username' & keyof User;
// 相当于:
type T = 'username' & keyof ('name' | 'age' | 'address');
- 计算左边 Omit 中的 Pick
Pick<User, 'name' & keyof User>
// 交叉类型运算符的结果跟第二步一样,返回 'name'
Pick<User, 'name'>
// 最终结果:
{
name: string
}
- 根据上一步得到的结果,计算
Partial的结果
Partial<{ name: string }>
// Partial 是内置工具函数,就是将类型 T 的全部属性变为可选的,则最终结果为:
{
name?: string
}
- 将
Omit内部&两侧的结果代入到类型中,则有:
// 因为 { name?: string } & { name: string } 是通过 & 运算符产生的类型
// 是两个类型的交集,所以需要将它们融合为一个类型
Omit<{ name?: string } & { name: string }>, never>;
// 使用 Omit 再进行一次运算,第二个参数可以传递 never
// 所以就达成将两个类型融为一体的目的了:
{
name?: string
age: number
address: string
}