如何解决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
相关产品推荐
相关产品推荐

