如何让类显式声明映射类型的可选字段并实现该类型?
如何让类显式声明映射类型的可选字段并实现该类型?
哎,这个需求我太懂了!之前写TS的时候也纠结过——明明定义了带可选字段的映射类型,结果类实现它的时候直接空着也能通过类型检查,但就是想让类里明明白白把每个可选字段都显式写出来,心里才踏实对吧?
下面给你几个实用的解决办法,按需选就行:
方案一:调整类型定义,强制字段必须存在
我们可以搞个辅助类型,把原来的可选映射类型里的「可选标记?」去掉,但保留字段的类型为string | undefined。这样一来,TypeScript就会要求类必须显式声明这些字段,同时允许它们的值是string或者undefined(和原来的可选字段行为一致)。
代码示例:
interface Fields { a: string; b: string; } // 辅助类型:把可选字段转为「必须存在、类型为原可选类型」的字段 type ExplicitOptional<T> = { [K in keyof T]-?: T[K] }; // 现在的Base要求必须包含所有Fields的键,类型是string | undefined type Base = ExplicitOptional<{ [k in keyof Fields]?: string }>; // 现在实现Base的话,必须显式写出a和b才行! class Sub implements Base { // 写成可选字段的形式,完全符合要求 a?: string; b?: string; // 或者写成必选但允许undefined的形式,也能通过 // a: string | undefined; // b: string | undefined; }
要是你敢把a或者b删掉,TS立马就会给你报错,完美满足“显式声明”的要求。
方案二:用类型约束双管齐下
要是不想改原来的Base类型定义,我们可以在类实现的时候,额外加一个类型约束,要求类必须包含Fields的所有键。
代码示例:
interface Fields { a: string; b: string; } // 保留你原来的Base类型不变 type Base = { [k in keyof Fields]?: string }; // 同时实现Base和Record<keyof Fields, unknown>,强制类必须有所有字段 class Sub implements Base, Record<keyof Fields, unknown> { a?: string; b?: string; }
这里的Record<keyof Fields, unknown>就像是个“监督员”,要求类里必须有a和b这两个字段,而Base则负责约束字段的类型,两者结合就达到了我们要的效果。
方案三:加个装饰器做运行时检查
如果不光想在类型层面约束,还想在运行时也确保类确实声明了这些字段,那可以整个装饰器来做检查:
interface Fields { a: string; b: string; } type Base = { [k in keyof Fields]?: string }; // 装饰器:检查类的原型上是否有指定的所有字段 function RequireExplicitFields<T extends string>(fields: T[]) { return function (constructor: Function) { fields.forEach(field => { if (!(field in constructor.prototype)) { throw new Error(`类 ${constructor.name} 必须显式声明字段 "${field}"!`); } }); }; } // 给类加上装饰器,传入需要检查的字段列表 @RequireExplicitFields(Object.keys(Fields) as (keyof Fields)[]) class Sub implements Base { a?: string; b?: string; }
要是你没写某个字段,代码运行的时候直接就会抛出错误,连上线的机会都不给😎
内容来源于stack exchange
相关产品推荐
相关产品推荐

