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

如何仅对对象属性做类型校验同时保留const推导的键类型?

TypeScript 保留具体键类型同时校验对象值的方案

核心问题

直接用Record<string, T>会丢失具体键的字面量类型,而as const又无法校验对象值的类型,需要找到兼顾两者的方法。

最优方案:使用泛型辅助函数

通过泛型函数约束值的类型,同时让TypeScript自动推导具体的键字面量类型,还能实现对象只读:

// 定义辅助函数,先固定值的类型,再推导键类型
function createReadonlyLibrary<Value>() {
    return <Obj extends Record<string, Value>>(obj: Obj): Readonly<Obj> => {
        return Object.freeze(obj);
    };
}

// 使用示例
const MY_LIB = createReadonlyLibrary<{foo: string, bar: string}>()({
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 123} // 触发错误:Type 'number' is not assignable to type 'string'
});

// 正确推导具体键联合类型
type LibKeys = keyof typeof MY_LIB; // 'id_1' | 'id_2'

原理说明

  1. 外层函数createReadonlyLibrary<Value>先指定对象值必须符合的类型Value
  2. 返回的内层函数会自动推导传入对象的具体键类型Obj,同时约束Obj的所有值必须匹配Value
  3. Object.freeze让对象变为只读,等效于as const的只读效果

替代方案:泛型类型约束+类型断言

如果不想用函数,也可以用泛型类型配合断言,但写法稍繁琐:

type ReadonlyLibrary<Value> = Readonly<Record<string, Value>> & {
    [K in keyof any]: K extends string ? Value : never;
};

// 使用时需要用类型断言让TS保留具体键
const MY_LIB = {
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 'bar'}
} as ReadonlyLibrary<{foo: string, bar: string}>;

type LibKeys = keyof typeof MY_LIB; // 'id_1' | 'id_2'

但这种方式需要手动写断言,不如辅助函数直观,不推荐在大型项目中使用。

为什么Record<string, T>无法满足需求?

当给对象标注Record<string, T>类型时,TypeScript会把对象的具体键字面量(如'id_1')拓宽为通用的string类型,导致keyof typeof只能得到string,无法保留具体的键联合类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:21:01