英文:
Make all object properties of a type optional
问题
根据给定的类型 T,如何使所有属性的类型为 object 变为可选的?
因此,对于以下类型:
interface MyObject {
name: string;
age: number;
favorites: {
color: string
}
}
type MyNewObject = ObjectPropertiesOptional<MyObject>;
它将返回:
interface MyNewObject {
name: string;
age: number;
favorites?: {
color: string
}
}
英文:
Given a type T how can I make all the properties that are object optional?
So for a type:
interface MyObject {
name: string;
age: number;
favorites: {
color: string
}
}
type MyNewObject = ObjectPropertiesOptional<MyObject>;
it returns
interface MyNewObject {
name: string;
age: number;
favorites?: {
color: string
}
}
答案1
得分: 4
以下是可能的实现:
type ObjectPropertiesOptional<T> = (
Partial<T> & { [K in keyof T as T[K] extends object ? never : K]: T[K] }
) extends infer O ? { [K in keyof O]: O[K] } : never;
整个实现都被包裹在(...) extends infer O ? { [K in keyof O]: O[K] } : never中,这是一个让编译器将相对丑陋的交叉类型(例如{foo: string} & {bar: number})转换为单一对象类型(例如{foo: string; bar: number})的技巧。这与https://stackoverflow.com/q/57683303/2887218中描述的Expand<T>相同的技术。
实际实现是将Partial<T>(使用PartialT的重映射版本进行交叉,该版本仅包含非object属性。(通过将键重映射为never,我们将其抑制)。
因此,对于{foo: string, bar: object},它将变成{foo?: string, bar?: object} & {foo: string},这等效于{foo: string, bar?: object}(并通过extends infer O技巧转换为它)。
让我们来测试一下:
interface MyObject {
name: string;
age: number;
favorites: {
color: string
}
}
type MyNewObject = ObjectPropertiesOptional<MyObject>;
/*
type MyNewObject = {
name: string;
age: number;
favorites?: {
color: string;
} | undefined;
}
*/
看起来不错。`name`和`age`属性仍然是必需的,但`favorites`属性现在是可选的。
英文:
Here's one possible implementation:
type ObjectPropertiesOptional<T> = (
Partial<T> & { [K in keyof T as T[K] extends object ? never : K]: T[K] }
) extends infer O ? { [K in keyof O]: O[K] } : never;
The whole thing is wrapped in (...) extends infer O ? { [K in keyof O]: O[K] } : never which is a trick to make the compiler convert a relatively ugly interesection type like {foo: string} & {bar: number} and turn it into a single object type like {foo: string; bar: number}. This is the same technique as Expand<T> as described in https://stackoverflow.com/q/57683303/2887218.
The actual implementation is to intersect Partial<T> (using the Partial<T> utility type to make every property optional) with a remapped version of T that only includes non-object properties. (By remapping a key to never we suppress it).
So for {foo: string, bar: object}, it would become {foo?: string, bar?: object} & {foo: string}, which is equivalent to {foo: string, bar?: object} (and converted to it by the extends infer O trick).
Let's test it out:
interface MyObject {
name: string;
age: number;
favorites: {
color: string
}
}
type MyNewObject = ObjectPropertiesOptional<MyObject>;
/*
type MyNewObject = {
name: string;
age: number;
favorites?: {
color: string;
} | undefined;
}
*/
Looks good. The name and age properties are still required, but the favorites property is now optional.
答案2
得分: 0
据我所知,在TypeScript中目前还不可能实现这个。
这是Github讨论的链接。
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论