TypeScript如何实现返回类型随options参数动态变化的函数签名
解决方案
核心思路是通过泛型关联错误收集器的输出类型,自动推导函数返回的错误类型,同时定义通用的收集器接口支持第三方自定义扩展。
步骤1:定义基础类型和通用错误收集器接口
这一步是实现可扩展能力的核心,任意第三方只要实现该接口即可自定义错误收集逻辑:
// 基础返回结果类型 type Result<T, E> = {ok: true, value: T} | {ok: false, value: E}; // 通用错误收集器接口,泛型E为最终返回的错误类型 interface ErrorCollector<E> { // 收集单个错误,返回值表示是否继续解析后续行 add: (error: string) => boolean; // 解析结束后返回最终收集到的错误结果 getErrors: () => E; }
步骤2:实现内置的single、multi收集器
// 单错误收集器:遇到第一个错误就终止,返回单个错误字符串 function single(): ErrorCollector<string> { let error: string | undefined; return { add: (e) => { error = e; return false; // 终止后续解析 }, getErrors: () => error! } } // 多错误收集器:支持最大错误数限制,返回错误数组 function multi(maxErrors?: number): ErrorCollector<string[]> { const errors: string[] = []; return { add: (e) => { errors.push(e); // 未设置上限或者未达上限则继续解析 return !maxErrors || errors.length < maxErrors; }, getErrors: () => errors } }
步骤3:定义parseCsv函数,处理默认值适配
通过泛型默认值和默认参数实现errors默认为single的效果:
// 解析参数类型,泛型E和收集器返回的错误类型关联 type ParseCsvOptions<E> = { trim?: boolean; errors?: ErrorCollector<E>; } // 泛型默认设置为string,对应默认single()收集器的返回类型 function parseCsv<E = string>( file: string, options: ParseCsvOptions<E> = {}, parseLineFn: (line: Array<string>) => Result<null, string> ): Result<null, E> { // 没有传errors时默认使用single收集器 const collector = options.errors ?? single() as unknown as ErrorCollector<E>; // 以下是解析逻辑示例,可替换为你自己的实现 const lines = file.split('\n'); for (const rawLine of lines) { const line = options.trim ? rawLine.trim() : rawLine; const parseRes = parseLineFn(line.split(',')); if (!parseRes.ok) { const continueParse = collector.add(parseRes.value); if (!continueParse) break; } } const errors = collector.getErrors(); // 有错误则返回失败,否则返回成功 if (Array.isArray(errors) ? errors.length > 0 : !!errors) { return { ok: false, value: errors }; } return { ok: true, value: null }; }
调用效果验证
完全符合你预期的类型推导:
// 不传errors参数,默认返回Result<null, string> const result1: Result<null, string> = parseCsv('a,b\nc,d', {}, () => ({ok: true, value: null})); // 显式传single(),返回Result<null, string> const result2: Result<null, string> = parseCsv('a,b\nc,d', {errors: single()}, () => ({ok: true, value: null})); // 传multi(),返回Result<null, Array<string>> const result3: Result<null, Array<string>> = parseCsv('a,b\nc,d', {errors: multi()}, () => ({ok: true, value: null})); // 传带上限的multi(10),返回Result<null, Array<string>> const result4: Result<null, Array<string>> = parseCsv('a,b\nc,d', {errors: multi(10)}, () => ({ok: true, value: null}));
第三方自定义收集器示例
任意开发者都可以通过实现ErrorCollector接口扩展能力,比如实现一个返回带行号的错误收集器:
// 自定义错误格式 interface LineError { lineIndex: number; message: string; } // 自定义收集器 function lineErrorCollector(): ErrorCollector<LineError[]> { const errors: LineError[] = []; let lineIndex = 0; return { add: (msg) => { errors.push({ lineIndex: lineIndex++, msg }); return true; // 始终继续解析 }, getErrors: () => errors } } // 调用时自动推导返回错误类型为LineError[] const customRes = parseCsv('a,b\nc,d', { errors: lineErrorCollector() }, () => ({ok: false, value: '格式错误'})); // customRes类型为 Result<null, LineError[]>
内容的提问来源于stack exchange,提问作者Kannan Goundan
相关产品推荐
相关产品推荐

