如何实现createFieldGroup函数基于asString属性的返回值类型推导?
我明白你的问题了——你需要让createFieldGroup的返回类型根据每个字段的asString属性动态决定是原类型还是string,同时还要保证validator的参数类型能正确推断自value。之前的思路卡在了如何单独提取每个字段的asString字面量类型,其实我们可以通过基于输入类型的映射转换来解决这个问题,不需要复杂的多类型参数推断。
具体实现步骤:
重新定义
FieldSpec,明确字面量类型的默认值
先把asString的类型约束为布尔字面量,并给它一个默认值false,这样当用户不传入asString时,默认保持原类型:type FieldSpec<T, AsString extends boolean = false> = { value: T; asString?: AsString; validator?: (v: T) => void; };定义返回类型的转换助手
用一个助手类型来单独处理每个字段的返回类型,判断asString是否为true字面量,是的话返回string,否则返回原value的类型:type FieldGroupReturn<Fields extends Record<string, FieldSpec<any, any>>> = { [K in keyof Fields]: Fields[K]['asString'] extends true ? string : Fields[K]['value']; };调整
createFieldGroup的类型声明
让函数直接推断输入的fields的类型,然后用上面的助手类型生成返回类型:declare function createFieldGroup<F extends Record<string, FieldSpec<any, any>>>( fields: F ): FieldGroupReturn<F>;
验证你的示例场景
现在测试你给出的例子,完全符合预期:
const x = createFieldGroup({ foo: { value: true, asString: true, validator(v) { // v的类型自动推断为boolean ✅ } }, bar: { value: 0, asString: false, validator(v) { // v的类型自动推断为number ✅ } } }); // x的类型自动推断为 { foo: string; bar: number } ✅
处理asString未传入的情况
如果某个字段省略了asString,会默认使用false,返回原value的类型:
const y = createFieldGroup({ baz: { value: 'hello', validator(v) { // v的类型自动推断为string ✅ } } }); // y的类型自动推断为 { baz: string } ✅
为什么之前的思路有问题?
你之前尝试用两个类型参数T和U分别推断value和asString,但TypeScript的类型推断很难同时对齐两个独立的Record类型的key。而直接基于输入的fields类型做映射转换,能更精准地提取每个字段的asString和value的字面量类型,避免了多参数推断的对齐问题。
另外,你尝试重构FieldSpec让它依赖一个包含value和asString的泛型参数,这种方式会破坏TypeScript的类型推断优先级——因为TypeScript更倾向于从最外层的类型参数开始推断,而不是嵌套的属性类型,所以这种方案的推断效果会变差。
内容来源于stack exchange

