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

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能正确完成类型窄化。

解决方案

有两种常用方法可以解决这个问题:

  1. 使用as const断言
    给传入的对象字面量添加as const,明确告知TypeScript该对象为只读状态,属性类型不会被拓宽:

    const si2 = inferObjectSchemaId({ schemaId: SchemaId.SCHEMA_1 } as const) // 类型为SchemaId.SCHEMA_1
    
  2. 调整泛型定义,直接从对象推断类型
    修改函数的泛型约束,让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 05:43:32