如何定义FilterChange泛型实现TypeScript属性类型自动推断?
问题场景
定义了如下Filters接口:
interface Filters { active: boolean name: string tags: string[] }
希望调用function setFilter(change: FilterChange<Filters>)时,实现以下类型校验效果:
// 合法调用: setFilter({filter: 'active', value: true}) setFilter({filter: 'name', value: 'a string'}) setFilter({filter: 'tags', value: ['some', 'strings']}) // 非法调用需报错: setFilter({filter: 'active', value: 'wrong type'}) setFilter({filter: 'unknown_property', value: '?'})
但定义FilterChange泛型时遇到问题:
第一种写法报错:
interface FilterChange<T, K = keyof T> { filter: K value: T[K] //Error: Type K cannot be used to index type T }
改成显式指定键的写法后,无法让TypeScript根据filter属性自动推断类型,必须手动传入FilterChange<Filters, 'active'>这类具体参数。
解决方案
方案1:将setFilter定义为泛型函数
不用单独定义FilterChange接口,直接在函数上声明泛型约束,让TypeScript自动推断键类型:
interface Filters { active: boolean name: string tags: string[] } function setFilter<K extends keyof Filters>(change: { filter: K; value: Filters[K] }) { // 函数逻辑 }
调用时,TypeScript会根据filter字段的值自动推断K的类型,进而约束value的类型完全匹配对应键的类型,非法调用会直接报错。
方案2:定义FilterChange为联合类型(映射生成)
如果需要单独复用FilterChange类型,可以通过映射类型生成每个键对应的类型联合:
interface Filters { active: boolean name: string tags: string[] } type FilterChange<T> = { [K in keyof T]: { filter: K; value: T[K] } }[keyof T] function setFilter(change: FilterChange<Filters>) { // 函数逻辑 }
这里的FilterChange<T>会被展开为{filter: 'active', value: boolean} | {filter: 'name', value: string} | {filter: 'tags', value: string[]}的联合类型,TypeScript会自动根据filter的值匹配对应的value类型,实现自动校验。
原写法失效原因
原接口写法中,K = keyof T意味着K是所有键的联合类型(比如'active' | 'name' | 'tags'),此时T[K]是所有值类型的联合(boolean | string | string[]),TypeScript无法将filter的具体值和value的类型做关联,所以既会报错,也无法实现自动推断。而上述两种方案都通过缩小类型范围,让filter和value的类型形成一一对应的绑定关系。
内容的提问来源于stack exchange,提问作者Chris

