【问题标题】:Guarantee that future properties of an `interface` will not have values of a particular type保证“接口”的未来属性不会有特定类型的值
【发布时间】:2020-11-12 10:49:54
【问题描述】:

我有一个经常记录的interface(它是一个分布式跟踪上下文)。我的项目也有一个特殊的类型来表示秘密,我想确保Secret 类型的对象永远不会添加到这个interface

如何限制添加到interface 的任何新属性的类型?

例如,下面是当前界面的样子:

interface TraceMetadata {
  foo: string;
  bar: number;
  time: Date;
}

我想要一个类型级别的保证,保证有人不会这样做:

interface TraceMetadata {
  foo: string;
  bar: number;
  time: Date;
  userSecret: Secret; // bad!
}

如何在 TypeScript 中做到这一点?

【问题讨论】:

    标签: typescript types


    【解决方案1】:

    据我所知,TypeScript 中并没有任何机制可以直接阻止这种情况。如果编译器允许您使用自引用接口声明,您可能会发生这种情况,但看起来 TS4.0 及更高版本不允许这样做(有关更多信息,请参阅microsoft/TypeScript#40315)。

    相反,您可以想出一个哨兵类型别名,当且仅当有人将Secret-valued 属性添加到TraceMetadata 接口时才会产生编译器警告。例如:

    type VerifyExtends<T, U extends T> = void;
    type ValueOf<T> = T[keyof T];
    type TraceMetadataProperties = ValueOf<TraceMetadata>;
    type AcceptableTraceMetadataProperties = Exclude<TraceMetadataProperties, Secret>;
    
    /* NOTE WELL! */
    // If there is an error in the next line of code, then someone has put a Secret
    // value into TraceMetadata.  You should find out who and tell them to
    // be ashamed of themselves.
    type TraceMetadataHasNoSecrets = VerifyExtends<AcceptableTraceMetadataProperties,
      TraceMetadataProperties // <-- error here if someone has been naughty
    >;
    

    VerifyExtends&lt;T, U&gt; 类型只允许您指定TU 类型,其中U extends TTraceMetadataProperties 类型是 TraceMetadata 中所有属性类型的并集,而 AcceptableTraceMetadataProperties 是删除了任何 Secret 值的类型。如果TraceMetadataProperties extends AcceptableTraceMetadataProperties,那么一切都很好,因为TraceMetadata 的每个属性都是可以接受的。否则,TraceMetadata 的属性可以接受,你会得到一个错误。

    错误出现在此标记类型别名中,而不是出现在 TraceMetadata 的违规属性上,这一事实是次优的。但是,如果您对 sentinel 类型的注释足够多,希望有人能够理解该警告的含义。


    让我们测试一下。

    有了这个定义,没有编译器警告:

    interface TraceMetadata {
      foo: string;
      bar: number;
      time: Date;
    }
    
    type TraceMetadataHasNoSecrets = VerifyExtends<AcceptableTraceMetadataProperties,
      TraceMetadataProperties // okay
    >;
    

    但是有了这个定义,就有:

    interface TraceMetadata {
      foo: string;
      bar: number;
      time: Date;
      userSecret: Secret; // bad!
    }
    
    type TraceMetadataHasNoSecrets = VerifyExtends<AcceptableTraceMetadataProperties,
      TraceMetadataProperties // error!
    //~~~~~~~~~~~~~~~~~~~~~~~ <--
    // Type 'ValueOf<TraceMetadata>' does not satisfy the constraint 
    // 'string | number | Date'.
    >;
    

    希望在实际存在编译器警告的情况下,有人会查看标记类型错误,并查看error here if someone has been naughtysomeone has put a Secret value into TraceMetadata 并能够纠正该问题。

    Playground link to code

    【讨论】:

    • 不错!有没有办法扩展此检查以涵盖某些属性本身可能是对象的情况,在这种情况下,我们需要验证 它们的 属性都不是秘密?我一直在玩ContainsSecret 类型检查,但它并没有像我期望的那样工作:``` type ContainsString = T extends string ? T : (T extends {[k: string]: ContainsString} ? T : never); const a: ContainsString = 2; // 这个(出乎意料的)类型检查 ```
    猜你喜欢
    • 2020-10-09
    • 1970-01-01
    • 2018-11-25
    • 2021-11-12
    • 2011-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-23
    相关资源
    最近更新 更多