Typescript 基于另一个属性添加额外的类型属性

Typescript 基于另一个属性添加额外的类型属性,typescript,types,discriminated-union,Typescript,Types,Discriminated Union,#1我有一个对象列的类型。列可以是可过滤的,也可以是不可过滤的,如果isFilterable为true,则类型Column应要求:filterType、isTopBarFilter?和选项(但仅当filterType为'SELECT'--2) 我使用types-union来完成这样的类型,它几乎可以正常工作 type FilterableColumn = { isFilterable: true; filterType: 'SELECT' | 'TEXT' | 'DATE'; opt

#1我有一个对象列的类型。列可以是可过滤的,也可以是不可过滤的,如果
isFilterable
true
,则类型
Column
应要求:
filterType
isTopBarFilter?
选项
(但仅当
filterType
'SELECT'
-
-2)

我使用types-union来完成这样的类型,它几乎可以正常工作

type FilterableColumn = {
  isFilterable: true;
  filterType: 'SELECT' | 'TEXT' | 'DATE';
  options: string[];
  isTopBarFilter?: boolean;
};

type NonFilterableColumn = {
  isFilterable: false;
};

type Column = (NonFilterableColumn | FilterableColumn) & {
  name: string;
};
但是:

  • 正如我前面提到的(#2
    应该需要
    选项
    ,只有当
    过滤器类型
    选择的时候。我曾尝试使用类型联合来实现这一点,但它变得很奇怪:
  • 如果我将
    isFilterable
    设置为false,TS不会提示不需要的属性(这是好的),但如果我通过了这些不需要的道具(这是坏的),也不会显示错误

  • 我的解决方案还强制通过
    isFilterable
    ,即使它是
    false
    ,正如我上面提到的,我只想在它是
    true
    时通过它

  • 有没有办法改进我的解决方案(或其他解决方案)以实现我在开始时所描述的目标(#1)?

    让我们看看分布规律如何影响您案例中的交点和并集的解算方式。第一,以下几点:

    type OuterUnionMemberA=(UnionMemberA | UnionMemberB)&IntersectedA;
    
    相当于:

    键入OuterUnionMemberA=(UnionMemberA和IntersectedA)|(UnionMemberB和IntersectedA);
    
    这反过来又使我们得出以下结论:

    type ComplexType=(outerRunionMemberA | IntersectedB)&outerPrinterSected;
    
    相当于这个复杂的联合体:

    type ComplexType=(UnionMemberA和IntersectedA和OuterIntersected)|(UnionMemberB和IntersectedA和OuterIntersected)|(IntersectedB和OuterIntersected);
    
    让我们手动解析别名,看看它给我们留下了什么:

    type ComplexType={
    filterType:'选择';
    选项:字符串[];
    isFilterable:true;
    extrop?:布尔值;
    名称:字符串;
    } | {
    过滤器类型:“文本”|“日期”|“编号”;
    isFilterable:true;
    extrop?:布尔值;
    名称:字符串;
    } | {
    isFilterable:false;
    名称:string
    }
    
    为了验证我们对这是相同类型的期望,让我们做一个等式测试:

    type isSupertype=ComplexType扩展ComplexTypeUnwrapped?真:假//真的
    类型isSubtype=ComplexTypeUnwrapped扩展ComplexType?真:假//真的
    
    所有这些都是为了明确以下几点:

  • 有两个鉴别属性(
    filterType
    isFilterable
  • 在这种情况下,不执行该操作 以上的组合证明是TypeScript的一个已确认的设计限制(请参阅源存储库中的和问题,这些问题导致前一个问题被提出)

    但是你能做些什么呢
    never
    解救:被禁止作为属性与拥有类型
    never
    几乎是一样的,因此简单地更改
    isFilterable
    属性以避免使
    isFilterable
    成为第二个鉴别属性(为简单起见省略额外的可选属性)应该可以做到:

    类型列=
    (
    {name:string,isFilterable:true,filterType:“选择”,选项:string[]}
    {name:string,isFilterable:true,filterType:“TEXT”|“DATE”}
    {name:string,isFilterable:never}|
    {name:string,isFilterable:false}//允许“仅名称”大小写
    )
    const notFilterableAll:Column={name:'col2',isFilterable:false};
    const notFilterableText:Column={name:'col2',filterType:'TEXT}//缺少属性“isFilterable”;
    const notfilterableleselect:Column={name:“col2”,filterType:“SELECT”,选项:[]}//缺少属性“isFilterable”;
    const notFilterableSelectMissingOpts:Column={name:“col2”,filterType:“SELECT”}//类型“选择”不可分配给类型“文本”|“日期”;
    const-selectFilterOk:Column={name:'col2',isFilterable:true,filterType:“SELECT”,选项:[]}//好啊
    const textFilter:Column={name:“col2”,isFilterable:true,filterType:“TEXT”}//好啊
    

    哦,那是个好主意。你偶然发现了受歧视的工会在交叉路口的行为。如果没有人比我强,我明天就起草一份答复。这里有一个证据证明,你的想法只适用于嵌套的歧视工会:@OlegValter谢谢你的回复。我一直试图根据您上面发送的代码片段改进我的代码,但它不适合,我无法处理它。如果你能帮我办好我的案子,我会很感激的。嗯,我想我被其他事情弄得心烦意乱了,对不起-谢谢你给我回电话:(我很快就会给它一个机会。我需要先搜索一个好的参考资料-我想我看到了TS源回购协议上关于类似问题的讨论。rry,我删除了上面的一条评论,因为在过去的时间里,我忘记了你的问题不是关于嵌套的有区别的联合,而是关于对象类型与交叉点的联合。这是一个单独的问题e问题(可能与问题#9919有关,但我需要首先调查)呸-终于说到了:)这个问题是不同的,但可以解决。对于编辑和随后的回滚,我深表歉意,我有一个小实验需要运行,请不要介意-谢谢!谢谢你详尽的回答。Idea with
    从不
    涵盖大多数情况,但仍然是
    {name:'col2}
    TS需要是可过滤的,但不应该:/@bastej-刚刚注意到我打错了,让读者想知道我的意思是什么:)Re:
    {name:string}
    case-是的,不幸的是,这是为解决方案付出的代价-我认为你现在不能完全解决它。至少它在团队的轨道上…@bastej-你可以在
    列中添加第四个工会成员type FilterableColumn = {
      isFilterable: true;
      filterType: 'SELECT' | 'TEXT' | 'DATE';
      options: string[];
      isTopBarFilter?: boolean;
    };
    
    type NonFilterableColumn = {
      isFilterable: false;
    };
    
    type Column = (NonFilterableColumn | FilterableColumn) & {
      name: string;
    };
    
    type FilterableSelectColumn = {
      filterType: 'SELECT';
      options: string[];
    };
    
    type FilterableNonSelectColumn = {
      filterType: 'TEXT' | 'DATE' | 'NUMBER';
    };
    
    type FilterableColumn = (FilterableSelectColumn | FilterableNonSelectColumn) & {
      isFilterable: true;
      isTopBarFilter?: boolean;
    };
    
    type NonFilterableColumn = {
      isFilterable: false;
    };
    
    type Column = (FilterableColumn | NonFilterableColumn) & {
      name: string;
    };
    
    // e.g
    const col: Column = {
      name: 'col2',
      isFilterable: false,
      filterType: 'SELECT', // unwanted
      isTopBarFilter: false, // unwanted
      options: ['option1'], // unwanted
    };