如何在不继承类的情况下正确推断类属性的类型?
问题解决:TypeScript保留类属性原始类型的方法
问题场景
以下代码中,getSchema函数返回的类型被TypeScript推断为每个属性都是所有属性类型的联合,但期望每个属性保留其原始具体类型:
class XClass { x = "x"; y = 11; b = true; } let xObj = new XClass(); function getSchema<T extends XClass>(instance: T): Record<keyof T, T[keyof T]> { const returnObj = {} as Record<keyof T, T[keyof T]>; for (const key in instance) { returnObj[key] = instance[key]; } return returnObj; } let schema = getSchema(xObj); type X = typeof schema; // 当前推断的类型: // type X = { // x: string | number | boolean; // y: string | number | boolean; // b: string | number | boolean; // } // 期望的类型: // type X = { // x: string; // y: number; // b: boolean; // }
问题原因
Record<keyof T, T[keyof T]>的类型定义存在问题:
T[keyof T]会提取T所有属性值类型的联合(这里是string | number | boolean)Record类型会把keyof T中的每个键都映射到这个联合类型,导致所有属性的类型都被统一为混合类型
解决方案
方案1:将返回类型改为T
直接让函数返回输入实例的类型T,并调整类型断言:
class XClass { x = "x"; y = 11; b = true; } let xObj = new XClass(); function getSchema<T extends XClass>(instance: T): T { const returnObj = {} as T; for (const key in instance) { returnObj[key] = instance[key]; } return returnObj; } let schema = getSchema(xObj); type X = typeof schema; // 推断结果符合预期:x:string, y:number, b:boolean
方案2:使用精确映射类型替代Record
显式使用映射类型{ [K in keyof T]: T[K] },为每个键保留对应的原始类型:
function getSchema<T extends XClass>(instance: T): { [K in keyof T]: T[K] } { const returnObj = {} as { [K in keyof T]: T[K] }; for (const key in instance) { returnObj[key] = instance[key]; } return returnObj; }
方案3:用Object.assign简化实现
Object.assign会自动推断正确的返回类型,无需手动循环和断言:
function getSchema<T extends XClass>(instance: T): T { return Object.assign({}, instance); }
说明
映射类型{ [K in keyof T]: T[K] }和T本质等价,都会完整复刻原类型的结构与属性类型。Record类型更适合所有键对应相同类型的场景,不适合需要保留属性独立类型的需求。
内容的提问来源于stack exchange,提问作者Nihfur lourag
相关产品推荐
相关产品推荐

