如何基于对象属性类型缩小泛型类型?TypeScript工具类型实现
实现TypeScript工具类型
ExtractOnProp:从联合类型中提取指定属性匹配目标类型的对象 我需要创建一个TypeScript工具类型ExtractOnProp,作用是从联合类型T中,提取出**指定属性Prop的类型属于目标类型ValueType**的对象。
示例如下:
type ExtractOnProp<T, Prop extends keyof T, ValueType> = /* 待实现 */; type Test1 = ExtractOnProp< | { value: "test1" | "test2", prop1: any } | { value: "test1", prop2: any } | { value: "test3", prop3: any }, "value", "test1">; // 期望Test1的结果:{ value: "test1", prop1: any } | { value: "test1", prop2: any };
实际应用场景示例(从数组中筛选匹配元素):
function narrow< T extends { value: string }, Value extends readonly T['value'][], >( elements: readonly T[], filter: Value, ): ExtractOnProp<T, 'value', Value[number]> { const result = elements.find((el) => filter.includes(el.value as any)); if (!result) { throw new Error('not found'); } return result as any; } narrow( [ { value: 'test1', prop1: 1 }, { value: 'test2', prop1: 2 }, { value: 'test3', prop3: 3 }, ] as const, ['test1', 'test3'] as const, ); // 期望返回类型:{ value: 'test1', prop1: 1 } | { value: 'test3', prop3: 3 }
尝试过的实现及问题
最初的错误实现
type ExtractOnProp<T, Prop extends keyof T, ValueType> = T[Prop] extends ValueType ? T : never; // 问题:始终返回never
属性类型交集实现(仅能提取属性类型)
type IntersectPropType<T, Prop extends keyof T, ValueType> = T[Prop] extends infer S ? S extends ValueType ? S : never : never; type Test1 = IntersectPropType<{ value: 'test1' | 'test2' } | { value: "test3"; prop: any }, 'value', 'test2' | 'omitted'>; // Test1 = 'test2'; // 问题:只能提取匹配的属性类型,无法返回完整的对象类型
编辑补充后的实现(接近但仍有问题)
type ExtractOnProp<T, Prop extends keyof T, ValueType> = T extends { [_ in Prop]: infer S; } ? ValueType extends S ? T : never : never; type Test = ExtractOnProp< { value: 'a' | 'c'; prop1: any } | { value: 'b'; prop2: any }, 'value', 'a' >; // 实际Test结果:{ value: 'a' | 'c', prop1: any } // 问题:能筛选出符合条件的联合成员,但无法将成员的`value`属性类型缩小为`'a'`
正确的实现方案
要解决这个问题,需要同时完成两个操作:
- 从联合类型中筛选出属性
Prop与ValueType有交集的成员 - 将筛选出的成员的
Prop属性类型缩小为ValueType与原属性类型的交集
实现代码如下:
type ExtractOnProp<T, Prop extends keyof T, ValueType> = T extends infer U ? U extends Record<Prop, infer S> ? (ValueType & S) extends never ? never : Omit<U, Prop> & { [P in Prop]: ValueType & S } : never : never;
验证结果
验证Test1
type Test1 = ExtractOnProp< | { value: "test1" | "test2", prop1: any } | { value: "test1", prop2: any } | { value: "test3", prop3: any }, "value", "test1">; // Test1结果:{ value: "test1", prop1: any } | { value: "test1", prop2: any } // 符合预期
验证Test
type Test = ExtractOnProp< { value: 'a' | 'c'; prop1: any } | { value: 'b'; prop2: any }, 'value', 'a' >; // Test结果:{ value: 'a', prop1: any } // 符合预期
验证实际应用场景
function narrow< T extends { value: string }, Value extends readonly T['value'][], >( elements: readonly T[], filter: Value, ): ExtractOnProp<T, 'value', Value[number]> { const result = elements.find((el) => filter.includes(el.value as any)); if (!result) { throw new Error('not found'); } return result as any; } const res = narrow( [ { value: 'test1', prop1: 1 }, { value: 'test2', prop1: 2 }, { value: 'test3', prop3: 3 }, ] as const, ['test1', 'test3'] as const, ); // res的类型:{ value: 'test1', prop1: 1 } | { value: 'test3', prop3: 3 } // 符合预期
内容的提问来源于stack exchange,提问作者Ritave
相关产品推荐
相关产品推荐

