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

TypeScript泛型布尔属性访问器类型不兼容问题求助

问题描述

我尝试编写类型安全的泛型函数,用于获取对象的布尔属性值,却遇到编译器报错,提示T[K]无法赋值给boolean类型。希望尽可能避免使用obj[propName] as unknown as boolean这类类型断言。

基础代码示例

type KeyOfType<T, V> = keyof {
  [P in keyof T as T[P] extends V ? P : never]: any
}

function getBooleanValue<T, K extends KeyOfType<T, boolean>>(obj: T, propName: K): boolean {
  return obj[propName]
//         ^
// Type 'T[K]' is not assignable to type 'boolean'.
//   Type 'T[keyof { [P in keyof T as T[P] extends boolean ? P : never]: any; }]' is not assignable to type 'boolean'.
//     Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'boolean'.
//       Type 'T[string]' is not assignable to type 'boolean'.(2322)
}

实际应用场景(泛型比较器)

type KeyOfType<T, V> = keyof {
  [P in keyof T as T[P] extends V ? P : never]: any
}

export function compareByBooleanProperty<T, K extends KeyOfType<T, boolean>>(
  propName: K,
): (a: T, b: T) => number {
  const propertyAccessor = (obj: T) => obj[propName]
  const booleanComparator = compareBooleans
  return (a: T, b: T) => {
    const aValue = propertyAccessor(a)
    const bValue = propertyAccessor(b)
    return booleanComparator(aValue, bValue)
//                            ^
// Type 'T[K]' is not assignable to type 'boolean'.
//   Type 'T[keyof { [P in keyof T as T[P] extends boolean ? P : never]: any; }]' is not assignable to type 'boolean'.
//     Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'boolean'.
//       Type 'T[string]' is not assignable to type 'boolean'.(2322)
  }
}

function compareBooleans(a: boolean, b: boolean): number {
  // 自定义比较逻辑
  return 0
}

type Person = {
  name: string
  isMarried: boolean
}

compareByBooleanProperty<Person, "isMarried">("isMarried") // 生成按isMarried属性排序的比较器

解决方案

问题根源

你定义的KeyOfType虽然能筛选出值类型匹配V的键,但TypeScript无法反向推导T[K]必然等于V——因为keyof { ... }最终会被解析为string | number | symbol的子集,TypeScript无法将这个子集与T的属性类型做精准关联。

修正后的代码实现

我们需要调整KeyOfType的定义方式,让它直接约束K对应的属性类型必须是V,同时让TypeScript能明确推导T[K]的类型:

// 重新定义KeyOfType,通过映射取值明确约束属性类型
type KeyOfType<T, V> = {
  [P in keyof T]: T[P] extends V ? P : never
}[keyof T]

// 修复后的getBooleanValue函数
function getBooleanValue<T, K extends KeyOfType<T, boolean>>(obj: T, propName: K): boolean {
  return obj[propName] // 编译器可正确识别T[K]为boolean类型
}

// 修复后的泛型比较器函数
export function compareByBooleanProperty<T, K extends KeyOfType<T, boolean>>(
  propName: K,
): (a: T, b: T) => number {
  const propertyAccessor = (obj: T) => obj[propName]
  const booleanComparator = compareBooleans
  return (a: T, b: T) => {
    const aValue = propertyAccessor(a)
    const bValue = propertyAccessor(b)
    return booleanComparator(aValue, bValue) // 无类型报错
  }
}

function compareBooleans(a: boolean, b: boolean): number {
  return a === b ? 0 : a ? 1 : -1 // 示例比较逻辑:true排在false后面
}

type Person = {
  name: string
  isMarried: boolean
}

// 使用示例:无需手动指定泛型参数,TypeScript会自动推导
compareByBooleanProperty("isMarried")

原理说明

新的KeyOfType通过映射类型取值的方式:先为每个键P生成对应的类型(匹配V则保留P,否则为never),再通过[keyof T]取出所有非never的键。这种方式让TypeScript能明确知道:当K extends KeyOfType<T, V>时,T[K]必然是V的子类型,从而彻底解决类型不匹配的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 04:52:56