You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 23:54:00