?内置的必填泛型不够灵活,RequiredByKeys 泛型来救场。
为了帮助读者更好地巩固 TypeScript 的知识,我从 Github 上的 type-challenges 库中选择了几十个挑战,与您一起完成类型挑战。
实现一个通用的??RequiredByKeys<T, K>
??方法,它接受两个类型参数??T
??和??K
?。
K
??指定了??T
??的属性集,这些属性应该设置为必需的。当没有提供??K
??时,它应该像正常的??Required<T>
??一样设置所有必需的属性。
例如:
interface User {
name?: string
age?: number
address?: string
}
type UserRequiredName = RequiredByKeys<User, 'name'>
// { name: string; age?: number; address?: string }
我们的类型挑战是实现??RequiredByKeys<T, K>
??泛型,当??K
??没有提供时,它应该像普通的??Required<T>
??一样提供所有所需的属性。
构造一个类型,其中包含??Type
??的所有属性设置为 required。
Required<T>
??是 TypeScript 的内置工具类型,在??typescript/lib/lib.es5.d.ts
??文件中定义:
/**
* Make all properties in T required.
* typescript/lib/lib.es5.d.ts
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
?Required<T>
??泛型在内部使用了 TypeScript 的映射类型,其语法如下
其中??P in K
??类似于 JavaScript 中的??for...in
??语句,用于遍历类型??K
??中的所有类型,以及类型变量??T
?,用于表示 TypeScript 中的任何类型。
你也可以在映射过程中使用额外的修饰符只读和问号(?) 。通过添加加号( )和减号(-)前缀来添加和删除相应的修饰符。如果不添加前缀,默认使用加号。
在介绍了映射类型的相关知识之后,实现??RequiredByKeys
??泛型的思路就非常简单了。
从上图可以看出,我们只需要选择与 K 相关的属性,按照需要设置并生成一个新的对象类型,然后根据剩下的属性构建另一个对象类型,最后使用??&
??操作符将上述两个对象类型组合成一个新的对象类型。
最后,让我们看一下完整的代码:
TypeScript 4.1 允许我们使用 as 子句在映射类型中重新映射键,其语法如下:
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
// ^^^^^^^^^^^^^
// New Syntax!
}
其中NewKeyType的类型必须是?string | number | symbol
?联合类型的子类型,在重新映射键的过程中,我们可以通过返回never类型来过滤键。
?Merge
??泛型的作用是合并对象类型,除了上述的解决方案,还有一种更简洁的方式。
type RequiredByKeys<T, K = keyof T> = Merge<
T & {
[P in keyof T as P extends K ? P : never]-?: T[P]
}
>
这个挑战涉及到的主要知识是 TypeScript 的映射类型,如果你想了解更多关于映射类型的信息,可以阅读以下文章:
如果你有其他的解决方案,你可以给我发消息。
?欢迎关注公众号:文本魔术,了解更多
?