据我所知,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<T, U> 类型只允许您指定T 和U 类型,其中U extends T。 TraceMetadataProperties 类型是 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 naughty 和someone has put a Secret value into TraceMetadata 并能够纠正该问题。
Playground link to code