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

无泛型下TypeScript实现参数与返回类型的关联映射

解决方案:用函数重载 + 类型守卫实现严格类型对应

既然你需要严格绑定Question/Response/Result的对应关系,同时还要保留完整的类型信息,函数重载绝对是最适合的方案——它能让TypeScript精准推断出返回值的类型,完全满足你的需求。

先给你完整的代码实现,再一步步解释:

首先我们先把基础接口和子类接口补全(给每个Question子类加个type字面量字段,方便后续做类型守卫,当然你也可以用其他特征来区分类型):

// 基础接口定义
interface Question {}
interface Response {}
interface Result {}

// 具体的问题类型
interface MultipleChoiceQuestion extends Question {
  type: 'multiple-choice';
  options: string[];
}

interface DropdownQuestion extends Question {
  type: 'dropdown';
  options: string[];
}

interface InputQuestion extends Question {
  type: 'input';
  placeholder: string;
}

// 对应的响应类型
interface MultipleChoiceResponse extends Response {
  selectedOption: string;
}

interface DropdownResponse extends Response {
  selectedOption: string;
}

interface InputResponse extends Response {
  inputValue: string;
}

// 对应的结果类型
interface MultipleChoiceResult extends Result {
  questionType: 'multiple-choice';
  selected: string;
}

interface DropdownResult extends Result {
  questionType: 'dropdown';
  selected: string;
}

interface InputResult extends Result {
  questionType: 'input';
  input: string;
}

接下来是核心的函数实现,我们用函数重载来定义三种合法的输入输出组合:

// 函数重载:明确每种Question+Response组合对应的Result类型
function createResult(question: MultipleChoiceQuestion, response: MultipleChoiceResponse): MultipleChoiceResult;
function createResult(question: DropdownQuestion, response: DropdownResponse): DropdownResult;
function createResult(question: InputQuestion, response: InputResponse): InputResult;

// 函数的实际实现(用宽泛的类型接收参数,内部做类型判断)
function createResult(question: Question, response: Response): Result {
  // 用类型守卫判断问题类型,TS会自动缩小类型范围
  if ('type' in question && question.type === 'multiple-choice') {
    // 这里TS能推断response应该是MultipleChoiceResponse,放心访问属性
    return {
      questionType: 'multiple-choice',
      selected: (response as MultipleChoiceResponse).selectedOption
    };
  }

  if ('type' in question && question.type === 'dropdown') {
    return {
      questionType: 'dropdown',
      selected: (response as DropdownResponse).selectedOption
    };
  }

  if ('type' in question && question.type === 'input') {
    return {
      questionType: 'input',
      input: (response as InputResponse).inputValue
    };
  }

  // 处理非法输入的情况
  throw new Error('Unsupported question or response type');
}

为什么这个方案好用?

  1. 类型安全:调用函数时,如果传入不匹配的Question和Response(比如把InputQuestion和MultipleChoiceResponse一起传),TypeScript会直接报错,提前拦截错误。
  2. 类型推断准确:当你传入合法的组合时,TS会自动推断返回值的具体类型,比如:
    const mcQ: MultipleChoiceQuestion = { type: 'multiple-choice', options: ['A', 'B'] };
    const mcR: MultipleChoiceResponse = { selectedOption: 'A' };
    const mcResult = createResult(mcQ, mcR);
    // mcResult的类型是MultipleChoiceResult,TS会提示它的questionType和selected字段
    
  3. 不需要泛型:完全符合你最初的限制(如果是因为某些原因不能用泛型的话)。

额外优化:自定义类型守卫

如果你的Question接口没有type这样的标识字段,也可以写自定义类型守卫函数来区分类型,比如:

function isMultipleChoiceQuestion(q: Question): q is MultipleChoiceQuestion {
  return 'options' in q && Array.isArray((q as MultipleChoiceQuestion).options);
}

// 然后在函数里用这个守卫:
if (isMultipleChoiceQuestion(question)) {
  // 这里question的类型已经被缩小为MultipleChoiceQuestion
  return {
    questionType: 'multiple-choice',
    selected: (response as MultipleChoiceResponse).selectedOption
  };
}

备选方案(如果允许用泛型)

要是你后来发现可以用泛型了,也可以用映射类型来简化代码,把类型关联逻辑集中起来:

// 定义类型映射关系
type QuestionType = 'multiple-choice' | 'dropdown' | 'input';

type QuestionMap = {
  'multiple-choice': MultipleChoiceQuestion;
  'dropdown': DropdownQuestion;
  'input': InputQuestion;
};

type ResponseMap = {
  'multiple-choice': MultipleChoiceResponse;
  'dropdown': DropdownResponse;
  'input': InputResponse;
};

type ResultMap = {
  'multiple-choice': MultipleChoiceResult;
  'dropdown': DropdownResult;
  'input': InputResult;
};

function createResult<T extends QuestionType>(
  question: QuestionMap[T],
  response: ResponseMap[T]
): ResultMap[T] {
  if (question.type === 'multiple-choice') {
    return { questionType: 'multiple-choice', selected: response.selectedOption } as ResultMap[T];
  }
  // 其他分支...
  throw new Error('Unsupported type');
}

这个方案更简洁,但依赖泛型,所以如果必须禁用泛型,还是回到函数重载的方案就好。

内容的提问来源于stack exchange,提问作者Mustafa Ekim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:22:46