如何窄化fp-ts中R.lookup方法推断的Option类型
fp-ts R.lookup 键查询类型推断异常问题解决
问题现象
使用fp-ts的R.lookup查询对象可选键对应值时,TypeScript推断结果不符合预期,复现代码如下:
import * as R from 'fp-ts/Record'; import * as O from 'fp-ts/Option'; type Data = { a?: number; b?: string; c?: { e?: string; w: number }; }; const e = R.lookup('b')({ a: 23, b: 'asdfasdf', c: { e: 'asdf', w: 23}} as Data)
预期e的类型为Option<string>,实际推断为Data所有属性值类型组成的联合包裹的Option:
O.Option<string | number | { e?: string | undefined; w: number; }>
尝试手动指定泛型参数<string>修正类型时,会抛出类型不兼容错误:
const getB = R.lookup("b") const e = getB<string>({ a: 234, b: "asdfasdf", c: { e: "asdf", w: 23 } } as Data); // 报错:Argument of type 'Data' is not assignable to parameter of type 'Record<string, string>'. // Property 'a' is incompatible with index signature. // Type 'number' is not assignable to type 'string'.
原因说明
这是R.lookup的正常设计表现:
柯里化调用场景下如果先传入键、后传入记录对象,TypeScript在接收第一个键参数的阶段,还无法获取后续传入对象的完整类型信息,不能建立「指定键-对应值类型」的映射关系,因此会把返回值类型推断为对象所有属性值类型的联合。
之前手动传入单个<string>泛型的写法错误,是因为R.lookup的泛型参数顺序为*<键类型, 记录对象类型>*,仅传入值类型会让TypeScript把整个记录的索引值类型错误约束为string,自然和包含多类型属性的Data类型冲突。
类型窄化方案
- 方案1:柯里化调用时按顺序传入完整泛型参数
显式指定键类型和记录对象类型,TS即可正确推导对应键的返回值类型:
如果需要得到纯// 按<键类型, 记录类型>顺序传入泛型 const getB = R.lookup<'b', Data>('b') const e = getB({ a: 234, b: "asdfasdf", c: { e: "asdf", w: 23 } } as Data) // e 类型为 Option<string | undefined>Option<string>类型,剔除值为undefined的场景,可以后续链式调用O.flatMap(O.fromNullable)处理。 - 方案2:非柯里化调用,同时传入键和记录对象
两个参数同时传入时,TS可以拿到完整的类型信息做推导,无需手动标注泛型:const data: Data = { a: 23, b: 'asdfasdf', c: { e: 'asdf', w: 23}} const e = R.lookup('b', data) // e 自动推导为 Option<string | undefined> - 方案3:管道流场景下可使用类型断言窄化
如果是在pipe流程中调用,确定键对应值类型的前提下,可以直接用类型断言修正类型,不影响后续管道操作:import { pipe } from 'fp-ts/function'; const data: Data = { a: 23, b: 'asdfasdf', c: { e: 'asdf', w: 23}} const e = pipe( data, R.lookup('b') ) as O.Option<string>
内容的提问来源于stack exchange,提问作者xav
相关产品推荐
相关产品推荐

