如何定义仅含指定类型值键的TypeScript IndexRecord类型
问题:实现严格的IndexRecord泛型类型
我想要创建一个名为IndexRecord的泛型类型,它接受一个继承自对象类型的类型参数。该类型仅允许包含输入类型中值类型继承自DocumentId的键,有效键的取值为true且每个条目均为可选,完全不允许存在无效键(即使设为undefined也不行)。
我目前写出的代码如下:
// Not an exact replica of KvObject and DocumentId, but contains the fundementals type DocumentId = string | number type Primitive = string | number | bigint | boolean type KvValue = Primitive | Primitive[] | KvObject type KvObject = { [key: string]: KvValue } // IndexRecord type I'm trying to define type IndexRecord<T extends KvObject> = Partial<{ [key in keyof T]: T[key] extends DocumentId ? true : never }> interface Data extends KvObject { big: bigint // Does not conform to DocumentId small: number // Does conform to DocumentId } // This seems to behave like I want it to const indexRecord1: IndexRecord<Data> = { big: true, // Error: type 'true' is not assignable to type 'undefined' small: true } // I want this to give an error for big, not for small. // // Error: // Property 'small' is incompatible with index signature // Type 'true' is not assignable to type 'undefined' const indexRecord2: IndexRecord<Data> = { big: undefined, // No error small: true }
当前写法中,indexRecord1的错误符合预期,但indexRecord2中设置big: undefined未报错,而small: true却报错,这不符合需求。请问是否可以实现我想要的类型定义?
解决方案
可以实现,核心思路是先筛选出有效键,再明确禁止无效键的存在。以下是修正后的类型定义:
// 基础类型定义 type DocumentId = string | number; type Primitive = string | number | bigint | boolean; type KvValue = Primitive | Primitive[] | KvObject; type KvObject = { [key: string]: KvValue; }; // 正确的IndexRecord类型定义 type IndexRecord<T extends KvObject> = // 第一步:仅保留T中值类型为DocumentId的键,值为true且可选 Partial<{ [K in keyof T as T[K] extends DocumentId ? K : never]: true }> & // 第二步:禁止所有无效键(即使设为undefined也不行) { [K in Exclude<keyof T, keyof Partial<{ [K in keyof T as T[K] extends DocumentId ? K : never]: true }>>]?: never }; interface Data extends KvObject { big: bigint; // 不符合DocumentId(无效键) small: number; // 符合DocumentId(有效键) } // 测试用例验证 // ❌ 错误:big是无效键,不允许存在 const indexRecord1: IndexRecord<Data> = { big: true, small: true }; // ❌ 错误:big是无效键,即使设为undefined也不允许 const indexRecord2: IndexRecord<Data> = { big: undefined, small: true }; // ✅ 正确:仅包含有效键small,值为true const indexRecord3: IndexRecord<Data> = { small: true }; // ✅ 正确:空对象,所有有效键都是可选的 const indexRecord4: IndexRecord<Data> = {};
原理说明
- 筛选有效键:通过映射类型的
as从句,仅保留T中值类型匹配DocumentId的键,构建一个值为true的类型,再用Partial让这些键变为可选。 - 禁止无效键:用
Exclude取出所有未被选中的无效键,定义这些键的类型为never(即使加上?设为可选)。TypeScript会认为任何对这些键的赋值(包括undefined)都是非法的,因为never类型不能被任何值赋值。
内容的提问来源于stack exchange,提问作者Noobster
相关产品推荐
相关产品推荐

