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

如何解决TypeScript序列定义中的返回类型推断与类型未知问题

TypeScript 序列步骤类型推断问题修复

初始问题

我尝试让TypeScript推断序列中每个action的返回类型,但遇到类型不兼容错误:

export type SequenceDefinition<S extends string> = {
  keys: S[] | Readonly<S[]>,
  actions: Record<S, <R>(data: SequenceData<S, SequenceDefinition<S>>) => R>
}

type SequenceData<S extends string, T extends SequenceDefinition<any>> = {
  get: (key: S) => ReturnType<T['actions'][S]>
}

export type SequenceCreatorFn = <S extends string>(config: SequenceDefinition<S>) => void;

export const sequence: SequenceCreatorFn = (config) => {
  console.log(config);
};

const result = sequence({
  keys: ['load', 'save', 'log'],
  actions: {
    load: () => { // <- 此处报错
      return { a: 1, b: 2 };
    },
    save: (data) => { // <- 此处报错
      data.get('load');
      return true;
    },
    log: () => { // <- 此处报错
      return 1;
    }
  }
});

报错信息

Type '() => { a: number; b: number; }' is not assignable to type '<R>(data: SequenceData<"load" | "save" | "log", SequenceDefinition<"load" | "save" | "log">>) => R'.
  Type '{ a: number; b: number; }' is not assignable to type 'R'.
    'R' could be instantiated with an arbitrary type which could be unrelated to '{ a: number; b: number; }'.

核心问题

  • action方法的类型定义错误,强制要求返回任意泛型R,但实际返回固定类型,导致类型不匹配
  • 需要实现data.get()能根据前置步骤的返回值进行强类型推断

更新后的进展

调整代码后,部分类型推断正常,但仍存在问题:

type TBaseSteps = Record<string, unknown>;

type SequenceDefinition<TSteps = TBaseSteps> = {
  keys: (keyof TSteps)[];
  steps: {
    [TKey in keyof TSteps]: (
      data: SequenceData<TSteps>
    ) => Promise<TSteps[TKey]>;
  };
};

type SequenceData<TSteps = TBaseSteps> = {
  get: <TKey extends keyof TSteps>(key: TKey) => Awaited<TSteps[TKey]>;
};

export const sequence = async <TSteps = TBaseSteps>(
  config: SequenceDefinition<TSteps>
): Promise<SequenceData<TSteps>> => {
  console.log(config);
  return undefined as any;
};

const result = await sequence({
  keys: ['load', 'save', 'refresh', 'log'],
  steps: {
    load: async () => {
      return { a: 1, b: 2 };
    },
    save: async (data) => {
      return true;
    },
    refresh: async () => {
      return 'refresh';
    },
    log: async (data) => {
      const load = data.get('load'); // <-- 类型推断正常
      const save = data.get('save'); // <-- 类型为unknown
      const refresh = data.get('refresh'); // <-- 类型推断正常,但如果refresh步骤添加data参数就会变成unknown

      return 1;
    },
  },
});

const load = result.get('load');
const save = result.get('save');
const refresh = result.get('refresh');

未解决问题

  • log步骤中调用data.get('save')时,类型显示为unknown
  • 若refresh步骤添加data参数,其返回类型也会变为unknown

解决方案

问题根源在于TypeScript无法自动推断包含函数参数的对象类型,可通过以下两种方式修复:

方案1:显式定义步骤类型

先声明所有步骤的返回类型,再传入sequence函数:

// 预定义所有步骤的返回类型
type AppSteps = {
  load: { a: number; b: number };
  save: boolean;
  refresh: string;
  log: number;
};

const result = await sequence<AppSteps>({
  keys: ['load', 'save', 'refresh', 'log'],
  steps: {
    load: async () => ({ a: 1, b: 2 }),
    save: async (data) => true,
    refresh: async () => 'refresh',
    log: async (data) => {
      const load = data.get('load'); // 类型为 { a: number; b: number }
      const save = data.get('save'); // 类型为 boolean
      const refresh = data.get('refresh'); // 类型为 string
      return 1;
    },
  },
});

方案2:优化类型定义,自动推断步骤类型

调整泛型约束,让TypeScript从步骤函数的返回值自动推断类型:

type SequenceDefinition<TSteps> = {
  keys: (keyof TSteps)[];
  steps: {
    [TKey in keyof TSteps]: (
      data: SequenceData<TSteps>
    ) => Promise<TSteps[TKey]>;
  };
};

type SequenceData<TSteps> = {
  get: <TKey extends keyof TSteps>(key: TKey) => Awaited<TSteps[TKey]>;
};

export const sequence = async <TSteps>(
  config: SequenceDefinition<TSteps>
): Promise<SequenceData<TSteps>> => {
  console.log(config);
  // 实际实现需存储步骤结果,此处用any占位
  return undefined as any;
};

// 无需显式传入泛型,TS自动推断所有步骤类型
const result = await sequence({
  keys: ['load', 'save', 'refresh', 'log'],
  steps: {
    load: async () => ({ a: 1, b: 2 }),
    save: async (data) => true,
    refresh: async (data) => 'refresh', // 添加data参数后类型仍正确
    log: async (data) => {
      const load = data.get('load'); // { a: number; b: number }
      const save = data.get('save'); // boolean
      const refresh = data.get('refresh'); // string
      return 1;
    },
  },
});

const load = result.get('load'); // { a: number; b: number }
const save = result.get('save'); // boolean
const refresh = result.get('refresh'); // string

原理说明

  • 移除TSteps的默认值TBaseSteps,避免TypeScript fallback到unknown类型
  • 让sequence函数的泛型TSteps完全由传入的config.steps推断,确保每个步骤的返回类型被正确捕获
  • 当步骤函数接收data参数时,TypeScript不再因为无法推断而将返回类型设为unknown,因为泛型TSteps已由整个steps对象的返回值确定

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 12:30:58