将 Typescript 接口中的所有属性设为可选

Posted

技术标签:

【中文标题】将 Typescript 接口中的所有属性设为可选【英文标题】:Make all properties within a Typescript interface optional 【发布时间】:2017-02-04 10:01:28 【问题描述】:

我的应用程序中有一个接口:

interface Asset 
  id: string;
  internal_id: string;
  usage: number;

这是帖子界面的一部分:

interface Post 
  asset: Asset;

我还有一个用于后期草稿的接口,其中资产对象可能只是部分构造

interface PostDraft 
  asset: Asset;

我想允许PostDraft 对象拥有部分资产对象,同时仍然检查那里的属性的类型(所以我不想只是将其与any 交换)。

我基本上想要一种能够生成以下内容的方法:

interface AssetDraft 
  id?: string;
  internal_id?: string;
  usage?: number;

无需完全重新定义Asset 接口。有没有办法做到这一点?如果不是,那么在这种情况下安排我的类型的聪明方法是什么?

【问题讨论】:

今天您需要手动创建第二个界面,尽管这可能会在不久的将来发生变化:如果您有兴趣,请查看 ts repo 上的partial types 问题。 【参考方案1】:

在 TypeScript

为此,请使用 TypeScript 默认提供的 Partial<T> 类型。

interface PostDraft 
    asset: Partial<Asset>;

现在asset 上的所有属性都是可选的,这将允许您执行以下操作:

const postDraft: PostDraft = 
    asset: 
        id: "some-id"
    
;

关于Partial&lt;T&gt;

Partial&lt;T&gt; 是 defined 的映射类型,它使所提供类型中的每个属性都是可选的(使用 ? 标记)。

type Partial<T> = 
    [P in keyof T]?: T[P];
;

详细了解映射类型 here 和 in the handbook。

深度局部

如果您想要在对象上递归工作的部分实现,那么您可以在 TS 4.1+ 中使用以下类型:

type DeepPartial<T> = 
    [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
;

【讨论】:

@david 可以走另一条路吗?根据需要标记以前不完整的字段?是否可以在每个字段的基础上进行? @Tony 是的,TS 2.8 中的条件类型是可能的(请参阅Required&lt;T&gt;)。 天哪,这是一个非常干净的解决方案。希望我早点知道这一点 继续对进入 TS 的想法感到惊讶......来自微软。希望他们的网络浏览器开发也有同样的意图(-_-)【参考方案2】:

如果我想要一个显式的AssetDraft 接口,我会使用extendsPartial 的组合:

interface Asset 
  id: string;
  internal_id: string;
  usage: number;


interface AssetDraft extends Partial<Asset> 

【讨论】:

这对我很有帮助!谢谢:D【参考方案3】:

接口中的属性要么是可选的,要么不是,你不能使用相同的接口一次作为可选和一次必须。

您可以做的是为AssetDraft 提供一个具有可选属性的接口,然后为Asset 提供一个具有强制属性的类:

interface AssetDraft 
    id?: string;
    internal_id?: string;
    usage?: number;


class Asset 
    static DEFAULT_ID = "id";
    static DEFAULT_INTERNAL_ID = "internalid";
    static DEFAULT_USAGE = 0;

    id: string;
    internal_id: string;
    usage: number;

    constructor(draft: AssetDraft) 
        this.id = draft.id || Asset.DEFAULT_ID;
        this.internal_id = draft.internal_id || Asset.DEFAULT_INTERNAL_ID;
        this.usage = draft.usage || Asset.DEFAULT_USAGE;
    

这里的默认值是静态成员,但您可以通过其他方式获取它们,或者在它们丢失时抛出错误。

当处理从服务器(或类似的东西)接收的 json 时,我发现这种方式非常舒服,接口代表 json 数据,类是使用 json 作为初始值构建的实际模型。

【讨论】:

是的,您可以..如 David Sherret 回答中所述...您必须使用泛型类型变量将您的接口作为 Partial ..T 传递给您的接口。 @greaterKing 现在可以轻松实现【参考方案4】:

除了David Sherret answer 之外,我只说了几行,如何在没有Partial&lt;T&gt; 类型的情况下直接实现它,以便更好地理解主题。

interface IAsset 
  id: string;
  internal_id: string;
  usage: number;


interface IPost 
  asset: IAsset;


interface IPostDraft 
  asset:  [K in keyof IAsset]?: IAsset[K] ;


const postDraft: IPostDraft = 
  asset: 
    usage: 123
  
;

【讨论】:

【参考方案5】:

如何强制投射一个空对象,例如

const draft = <PostDraft>
draft.id = 123
draft.internal_id = 456
draft.usage = 789

如果你真的需要这个,那么你总是可以从一个模板生成一个 d.ts 接口,该模板同时具有可选属性和类型属性。

正如 Nitzan 指出的那样,Typescript 接口属性是可选的,或者不是

【讨论】:

以上是关于将 Typescript 接口中的所有属性设为可选的主要内容,如果未能解决你的问题,请参考以下文章

将“NSManaged public var”设为可选布尔值

使用 Pydantic 将每个字段设为可选

根据发货国家/地区将 Woocommerce 结帐电话字段设为可选

TypeScript深入学习TypeScript对象类型

在 WooCommerce 中选择“本地取货”时,将结帐字段设为可选

TypeScript中的实用工具类型(Utility Types)