You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何定义仅含指定类型值键的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> = {};

原理说明

  1. 筛选有效键:通过映射类型的as从句,仅保留T中值类型匹配DocumentId的键,构建一个值为true的类型,再用Partial让这些键变为可选。
  2. 禁止无效键:用Exclude取出所有未被选中的无效键,定义这些键的类型为never(即使加上?设为可选)。TypeScript会认为任何对这些键的赋值(包括undefined)都是非法的,因为never类型不能被任何值赋值。

内容的提问来源于stack exchange,提问作者Noobster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 13:50:33