基于fp-ts的Option<string>数组转string数组及SVG路径命令验证类型优化问题问询
validationErrors的类型并优化fp-ts验证代码 先看你代码里的核心问题:当前validationErrors被推断为(string | undefined)[],是因为你用了option.fold(() => undefined, some => some)——虽然你已经过滤了Some项,但fold的返回类型还是包含undefined。我们可以通过更精准的类型缩小来解决这个问题,同时优化代码:
1. 修复validationErrors的类型
直接在过滤Some项后,用option.get提取值即可,因为此时数组内的元素已经都是Some<string>,TypeScript能准确推断出类型:
import * as option from 'fp-ts/Option'; import * as fpArray from 'fp-ts/Array'; import * as either from 'fp-ts/Either'; import * as fpFunction from 'fp-ts/function'; // ... 你的Command、CommandArray等类型定义 type CommandValidation = (commands: CommandArray, nextCommand: Command) => option.Option<string>; const newCommandValidations: CommandValidation[] = [ validateFirstCommandIsMove, validateSymmetricCommandFollowsBezier, validateNoMoveAfterMove ]; export const safelyPushCommand = (command: Command) => either.chain((commands: CommandArray) => { const validationErrors: string[] = fpFunction.pipe( newCommandValidations, fpArray.map(validation => validation(commands, command)), // 过滤后,数组元素类型缩小为Some<string>[] fpArray.filter(option.isSome), // 直接提取Some的值,得到明确的string[] fpArray.map(option.get) ); return validationErrors.length > 0 ? either.left(validationErrors) : either.right(pushCommands([command])(commands)); });
另外注意:你原来用了either.map,但回调函数返回的是Either类型,这会导致最终类型变成Either<E, Either<string[], CommandArray>>(嵌套的Either),所以应该换成either.chain,它会自动扁平化嵌套的Either结构。
2. 是否需要用apply函数传递参数?
当前的写法已经很清晰,不是必须用apply。但如果你想让验证函数更符合函数式组合的风格,可以将其柯里化:
// 改为柯里化的验证函数类型 type CommandValidation = (commands: CommandArray) => (nextCommand: Command) => option.Option<string>; // 然后可以用apply来批量传递commands参数 const validationErrors: string[] = fpFunction.pipe( newCommandValidations, // 给所有验证函数传入commands参数,得到(nextCommand) => Option<string>的数组 fpArray.map(fpFunction.apply(commands)), // 传入command参数,得到Option<string>的数组 fpArray.map(validate => validate(command)), fpArray.filter(option.isSome), fpArray.map(option.get) );
这种写法更偏向函数式风格,但可读性因人而异,你可以根据自己的习惯选择。
3. 当fold的第二个参数是恒等函数时的简洁写法
如果是处理单个Option(且可能为None),当fold的第二个参数是恒等函数时,确实可以用更简洁的option.toUndefined或option.getOrElse(() => undefined)来替代,它们的效果和你写的fold完全一致。但在我们这个场景下,因为已经过滤了Some项,所以直接用option.get是最直接高效的——既不需要处理None分支,又能得到明确的类型。
内容的提问来源于stack exchange,提问作者J. Barca

