TypeScript泛型:从对象属性推断枚举值的问题
问题:传入包含SchemaId的对象时类型无法被正确窄化
我需要根据Schema ID验证命令并返回类型化的验证结果,因此需要推断Schema ID的枚举值。我知道可以使用TSchemaId extends SchemaId这类泛型实现,直接传入Schema ID时能正确窄化类型,但传入包含该属性的对象时,类型无法被窄化。以下是代码示例:
enum SchemaId { SCHEMA_1 = 'SCHEMA_1', SCHEMA_2 = 'SCHEMA_2', } function inferSchemaId<TSchemaId extends SchemaId> (schemaId: TSchemaId): TSchemaId { return schemaId } function inferObjectSchemaId< TSchemaId extends SchemaId, TObjectWithSchemaId extends { schemaId: TSchemaId }, > (objectWithSchemaId: TObjectWithSchemaId): TSchemaId { return objectWithSchemaId.schemaId } const si1 = inferSchemaId(SchemaId.SCHEMA_1) // SchemaId.SCHEMA_1 const si2 = inferObjectSchemaId({ schemaId: SchemaId.SCHEMA_1 }) // SchemaId
如上述代码所示,si1被正确窄化为SchemaId.SCHEMA_1,但si2的类型无法被窄化。请问这是什么原因?我遗漏了什么?
原因与解决方案
原因
TypeScript处理对象字面量时,默认会将属性类型拓宽至整个枚举类型(此处为SchemaId),而非保留具体的枚举成员。这是因为TypeScript默认假设对象字面量可能被后续修改,因此放宽类型以兼容后续赋值操作。而直接传入枚举成员时,TypeScript会保留其具体字面量类型,所以inferSchemaId能正确完成类型窄化。
解决方案
有两种常用方法可以解决这个问题:
使用
as const断言
给传入的对象字面量添加as const,明确告知TypeScript该对象为只读状态,属性类型不会被拓宽:const si2 = inferObjectSchemaId({ schemaId: SchemaId.SCHEMA_1 } as const) // 类型为SchemaId.SCHEMA_1调整泛型定义,直接从对象推断类型
修改函数的泛型约束,让TypeScript直接从传入对象中提取schemaId的具体类型,无需预先声明泛型参数:function inferObjectSchemaId<T extends { schemaId: SchemaId }>(objectWithSchemaId: T): T['schemaId'] { return objectWithSchemaId.schemaId } const si2 = inferObjectSchemaId({ schemaId: SchemaId.SCHEMA_1 }) // 类型为SchemaId.SCHEMA_1这种方式让TypeScript自动提取对象中
schemaId的具体枚举成员类型,无需额外断言。
内容的提问来源于stack exchange,提问作者Guillaume Docquier
相关产品推荐
相关产品推荐

