【问题标题】:How to declare an optional generic type in an interface?如何在接口中声明可选的泛型类型?
【发布时间】:2020-03-13 02:59:21
【问题描述】:

我有这个界面:

export interface AlertInteraction<T> {
  id: AlertId;
  data: T;
  buttonId: AlertButtonId;
}

但有时不需要数据。我知道我可以将其声明为data: T,但我想知道我是否可以这样:

export interface AlertInteraction<T> {
  id: AlertId;
  data: T;
  buttonId: AlertButtonId;
}

export interface AlertInteraction {
  id: AlertId;
  buttonId: AlertButtonId;
}

所以,如果我给T,那么我假设我想访问数据,如果没有,那么假设它不存在。有可能吗?

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    为了实现这一点,我们需要使用条件类型。

    type AlertId = string; // just example type alias
    type AlertButtonId = string; // just example type alias
    type AlertInteraction<T = undefined> = {
      id: AlertId;
      buttonId: AlertButtonId;
    } & (T extends undefined ? {} : {
      data: T;
    })
    
    
    // example usage
    // no need for data (T is undefined)
    const a: AlertInteraction = {
      id: '1',
      buttonId: '1'
    }
    // data prop is required 
    const b: AlertInteraction<number> = {
      id: '1',
      data: 1,
      buttonId: '1'
    }
    

    最重要的是这部分:

    & (T extends undefined ? {} : {
      data: T;
    })
    

    如果 T 可分配给未定义,我们有条件地加入或输入{},或者我们为其他类型加入{data: T}。结果如果我们不设置泛型,默认为undefined,我们的类型构造函数计算为:

    // For T == undefined
    {
      id: AlertId;
      buttonId: AlertButtonId;
    } & {}
    // what is equal to just:
    {
      id: AlertId;
      buttonId: AlertButtonId;
    }
    

    如果提供的参数(通用)与undefined 不同,那么最终类型具有以下形式:

    // For T != undefined
    {
      id: AlertId;
      buttonId: AlertButtonId;
    } & { data: T}
    // which is equal to
    {
      id: AlertId;
      buttonId: AlertButtonId;
      data: T;
    }
    
    

    其中T 是由类型构造函数的参数给出的类型。还要清除信息 - 类型构造函数 = 任何带有泛型参数的类型定义。

    【讨论】:

    • 我们不能只写:export interface AlertInteraction { id: AlertId;数据?:T;按钮标识:警报按钮标识; }。那么我们应该能够在不声明类型的情况下使用接口
    • 非常好的实现。你真的帮了我!
    【解决方案2】:

    创建第二个包装类型,当它提供一个类型时它不会省略数据,但当没有提供类型时,则省略数据字段。

    正如您所说,将数据设为可选并不是正确的做法,因为您需要该字段在您不需要时根本不存在。

    export interface IInterationData<T> {
      id: AlertId;
      data: T;
      buttonId: AlertButtonId;
    }
    
    type IAlertInteraction<T = undefined> = T extends undefined
      ? Omit<IInterationData<any>, "data">
      : IInterationData<T>;
    
    export const a: IAlertInteraction = {
      id: 1,
      buttonId: ""
    };
    
    export const b: IAlertInteraction<number> = {
      id: 1,
      data: 2,
      buttonId: ""
    };
    

    【讨论】:

      【解决方案3】:

      你可以试试这样的

      export interface AlertInteraction<T = any> {
        id: AlertId;
        data?: T;
        buttonId: AlertButtonId;
      }
      
      // those are valid:
      
      const x: AlertInteraction = {id: 'AlertId', buttonId: 'AlertButtonId'};
      
      // Accept the data property as number only.
      const x: AlertInteraction<number> = {id: 'AlertId', buttonId: 'AlertButtonId', data: 123};
      
      

      或者你可以让它们成为 2 个接口,一个没有 data 属性和通用参数,第二个扩展第一个并具有数据属性和通用参数

      类似的东西

      
      export interface AlertInteractionBase {
        id: string;
        buttonId: string;
      }
      
      export interface AlertInteraction<T> extends AlertInteractionBase {
        data: T;
      }
      

      【讨论】:

      • &lt;T = any&gt; 的问题是,如果我执行x.,我仍然会得到data 作为自动完成功能。
      • 您必须将它们分成两个接口,并像第二种解决方案一样在他的情况下使用每个接口
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-12
      • 1970-01-01
      • 2015-11-04
      • 2018-10-03
      • 2018-12-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多