如何在TypeScript中推断对象字面量类型且避免readonly修饰符
如何让TypeScript泛型推断字面量类型且不带readonly修饰符
问题需求
需要将对象的所有属性推断为字面量类型,但不希望生成的类型带有readonly属性,主要用于优化IntelliSense体验,减少复杂结构的视觉负担。
基础问题示例
type Options = { min?: number; }; class E1<O extends Options> { constructor(options: O) {} } const e1 = new E1({ min: 1 });
此时typeof e1的结果不符合期望:
E1<{ min: number; // 期望是`1`而非number }>
使用const类型参数的尝试
TypeScript 5.0的const类型参数能实现字面量推断,但会自动添加readonly修饰符:
class E2<const O extends Options> { constructor(options: O) {} } const e2 = new E2({ min: 1 });
typeof e2的结果:
E2<{ readonly min: 1; // 类型正确但带有readonly }>
期望的最终结果
EN<{ min: 1; }>
无效的尝试方案
方案一:构造函数参数移除readonly
试图在构造函数参数中映射移除readonly,但泛型仍保留readonly属性:
class E3<const C extends Options> { constructor(c: { -readonly [K in keyof C]: C[K] }) {} } const e3 = new E3({ min: 1 });
方案二:通过类型工具约束泛型
定义移除readonly的类型工具,但泛型推断结果依旧带有readonly:
type NoRead<T> = { -readonly [K in keyof T]: T[K]; }; class E4<const C extends NoRead<Options>> { constructor(c: C) {} } const e4 = new E4({ min: 1 });
可行但繁琐的方案
对单个属性添加as const断言能得到期望类型,但对象结构复杂时操作繁琐:
class E5<C extends Options> { constructor(c: C) {} } const e5 = new E5({ min: 1 as const });
解决方案:工厂函数+类型转换
通过工厂函数利用const参数推断字面量,再将类型转换为不带readonly的版本:
type Options = { min?: number; }; // 移除readonly的类型工具 type Writable<T> = { -readonly [K in keyof T]: T[K] }; class EN<O extends Options> { constructor(options: O) {} } // 工厂函数:用const参数推断字面量,再转换为可写类型 function createEN<const O extends Options>(options: O): EN<Writable<O>> { return new EN(options); } // 测试 const en = createEN({ min: 1 }); // typeof en: EN<{ min: 1 }>
或者直接在类的泛型中处理(适合直接实例化的场景):
type Options = { min?: number; }; type Writable<T> = { -readonly [K in keyof T]: T[K] }; class EN<O extends Options> { constructor(options: O extends infer U ? Writable<U> : never) {} } const en = new EN({ min: 1 }); // typeof en: EN<{ min: 1 }>
内容的提问来源于stack exchange,提问作者Janek Eilts
相关产品推荐
相关产品推荐

