如何定义限制额外未声明键的TypeScript扩展接口?
如何定义限制额外键的类型/接口,同时兼容父类型?
我有一个定义路由的标准接口:
interface Routes { [key: string]: string | Routes; }
我想为每个模块定义子类型,比如:
interface GeoRoutes extends Routes { country: { list: string; }; }
目前代码能正常运行,但我希望禁止GeoRoutes对象包含未定义的额外键。比如下面的声明现在能编译通过,但我想让它编译失败:
const apiRoutes: GeoRoutes = { foo: 'bar', country: { list: '/countries', }, };
核心需求是:子类型既要能在处理Routes的方法中使用(比如function addRoute(routes: Routes) { ... }),又要严格限制只能包含指定的键。
因为Routes带有字符串索引签名,直接继承它的接口会自动允许任意字符串键——这是TypeScript的设计规则:只要接口继承了带索引签名的类型,就会继承这个“允许任意键”的特性。要实现严格键约束,你可以用以下几种方法:
方案一:交叉类型+严格定义
先写好模块专属的严格路由类型,再和Routes做交叉,这样既保证类型能传给Routes参数,又能限制额外键:
type StrictGeoRoutes = { country: { list: string; }; } & Routes; // 编译报错:对象文字只能指定已知属性,'foo' 不在类型 'StrictGeoRoutes' 中 const apiRoutes: StrictGeoRoutes = { foo: 'bar', country: { list: '/countries', }, }; // 正常编译:StrictGeoRoutes 兼容 Routes 类型 function addRoute(routes: Routes) {} addRoute(apiRoutes);
方案二:用satisfies关键字(TypeScript 4.9及以上)
如果不想改类型结构,可以用satisfies确保对象符合严格的模块路由结构,再通过类型断言兼容Routes:
type GeoRoutesStrict = { country: { list: string; }; }; // 编译报错:对象文字只能指定已知属性,'foo' 不在类型 'GeoRoutesStrict' 中 const apiRoutes = { foo: 'bar', country: { list: '/countries', }, } satisfies GeoRoutesStrict as Routes; // 正常编译 addRoute(apiRoutes);
方案三:工具类型移除索引签名
如果必须用接口继承,可以写个工具类型去掉父接口的索引签名,再合并:
type RemoveIndexSignature<T> = { [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K] }; interface GeoRoutesStrict { country: { list: string; }; } type GeoRoutes = GeoRoutesStrict & Routes; // 编译报错:'foo' 不在类型 'GeoRoutes' 中 const apiRoutes: GeoRoutes = { foo: 'bar', country: { list: '/countries', }, };
这些方案的核心都是把“严格键约束”和“父类型兼容性”分开处理,避开继承索引签名带来的宽松限制。
内容的提问来源于stack exchange,提问作者budgw
相关产品推荐
相关产品推荐

