【问题标题】:typescript interface conditional optional parameterstypescript 接口条件可选参数
【发布时间】:2018-09-06 13:01:59
【问题描述】:

我们能做到吗:

interface IMyInterface{
    firstname:string  // this means firstname is mandatory

    name:string  // this also means name is mandatory
}

我怎么说,firstnamename 之一是可选的 (?),取决于是否提供了另一个?

或者如果这不可能,还有什么其他选择?

编辑: 这不是 Typescript Interface - Possible to make "one or the other" properties required? 的副本。

我们不想仅仅因为维护和命名以及重构将是一件令人头疼的事情并且不可重用,就为每个可选元素创建单独的接口。

【问题讨论】:

标签: typescript


【解决方案1】:

你可以这样写。

interface ContactName{
    firstname?: string;
    name: string;
}

interface ContactFirstName{
    firstname: string
    name?: string
}

type Contact = ContactName | ContactFirstName;

当您使用联系人界面时,姓名或名字成为必填项。

【讨论】:

    【解决方案2】:

    如果IMyInterface 有其他成员需要保留,我想提出一个更通用的@Catalyst 响应版本。

    type EachExpanded<T> = {
        [key in keyof T]: { [subKey in key]: T[key]; }
    };
    
    type FixedSubset<T, U> = Pick<T, Exclude<keyof T, U>>;
    
    type AtLeastSubset<T, U> = Pick<T, Extract<keyof T, U>>;
    
    type AtLeaseOne<T, U> = FixedSubset<T, U> & EachExpanded<AtLeastSubset<T, U>>[keyof AtLeastSubset<T, U>];
    
    const example1: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
        { a: 3, b: 4, c: '4' } // valid
    const example2: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
        { a: 1, c: '1' } // valid
    const example3: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
        { b: 2, c: '2' } // valid
    const example4: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
        { c: '3' } // invalid
    

    请记住,此响应使用刚刚发布的 TypeScript 2.8 版中引入的 ExcludeExtract 关键字。这些是条件类型的一部分。

    在代码中,我们假设类型 T 是原始类型或接口,U 是一组键,其中至少一个必须存在。

    它的工作方式是通过排除需要基于原始类型T实现的属性来创建一个类型。这是使用 Pick&lt;T, Exclude&lt;keyof T, U&gt;&gt; 定义的,其中包含不在 U 中的所有内容。

    然后,创建另一种类型,只包含必须至少存在一个的元素,Pick&lt;T, Extract&lt;keyof T, U&gt;&gt;

    EachExpanded 将每个特殊集合的类型存储在同一键下。例如,如果键 'a''b' 将成为上述示例的有条件可选,EachExpanded 创建以下类型:

    {
        a: { a: number; };
        b: { b: number; };
    }
    

    这将在最终类型中与交集运算符一起使用,因此至少要强制存在其中一个。

    基本上,对于上面的示例,我们将得到以下结果:

    { c: string; } & ({ a: number; } | { b: number; })
    

    【讨论】:

      【解决方案3】:

      另一种方式:

          interface IName {
              name:string 
          }
      
          interface IFirstName {
              firstname:string 
          }
      
          let x: IName | IFirstName;
          x = {}; // Error
          x = { name: "" }; // Ok
          x = { firstname: "" }; // Ok
          x = { name: "", firstname: "" }; // Ok
      

      【讨论】:

      【解决方案4】:

      以下是表示“OneOf”这些键的通用方式,您可以在这里使用它:

      type EachOfTmp<T> = {// to make OneOf less gross
        [K in Keys<T>]: {
          _: {[X in K]: T[K]};
        }
      };
      
      // require only one of the keys
      export type OneOf<T> = EachOfTmp<T>[Keys<T>]["_"] & Partial<T>;
      
      const thing1: OneOf<{ a: number; b: number }> = { a: 2 } // valid
      const thing2: OneOf<{ a: number; b: number }> = { b: 2 } // valid
      const thing3: OneOf<{ a: number; b: number }> = {} // invalid
      

      编辑:哎呀,我忘了我用这个方便的钥匙交易 -

      export type Keys<T> = keyof T;
      export function Keys<T>(o: T) {
        if (!o) {
          return [];
        }
        return Object.keys(o) as Keys<T>[];
      }
      

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-21
      • 2022-08-06
      • 2015-02-15
      • 2023-01-25
      • 2017-10-21
      • 1970-01-01
      • 2011-02-03
      • 2017-10-18
      相关资源
      最近更新 更多