使类型的所有对象属性变为可选的。

huangapple go评论56阅读模式
英文:

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&lt;MyObject&gt;;

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>(使用Partial实用类型使每个属性可选)与T重映射版本进行交叉,该版本仅包含非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&lt;T&gt; = (
    Partial&lt;T&gt; &amp; { [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} &amp; {bar: number} and turn it into a single object type like {foo: string; bar: number}. This is the same technique as Expand&lt;T&gt; as described in https://stackoverflow.com/q/57683303/2887218.

The actual implementation is to intersect Partial&lt;T&gt; (using the Partial&lt;T&gt; 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} &amp; {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&lt;MyObject&gt;;
/*
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.

Playground link to code

答案2

得分: 0

据我所知,在TypeScript中目前还不可能实现这个。

这是Github讨论的链接。

英文:

As far as I am aware, this is not yet possible in Typescript.

Here's the Github discussion.

huangapple
  • 本文由 发表于 2023年3月7日 12:43:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75658115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定