TypeScript嵌套选择器返回值类型推断问题解决方案
问题描述
我希望实现嵌套选择器的值类型推断,即第二个选择器会将第一个选择器的返回结果作为入参,从该结果中选取子属性片段的值。
初始实现代码如下:
function store< T, Selector extends (a: T) => unknown, ReturnValue extends ReturnType<Selector>, NestedSelector extends (a: ReturnValue) => unknown, ReturnValueNestedSelector extends ReturnType<NestedSelector> >(state: T, selector: Selector) { const get = () => selector(state) as ReturnValue; const getNested = (nestedSelector: NestedSelector) => nestedSelector(selector(state) as ReturnValue) as ReturnValueNestedSelector; return { get, getNested, }; } const initialData = { foo: { bar: { id: 1, }, }, }; const myStore = store(initialData, (a) => a.foo.bar); const a = myStore.get(); // { id: number } const result = myStore.getNested((state) => { return state.id; // number }); // unknown
当前问题:第一个选择器的类型推断效果基本可用(仍需对返回值做类型断言),但getNested函数的返回值被推断为unknown,需要找到TypeScript下可行的解决方案。
问题原因
出现返回值unknown的核心原因是泛型声明位置错误:
- 嵌套选择器
NestedSelector和对应返回值类型被声明在store函数的顶层泛型中,这类泛型会在调用store初始化时就需要完成推断 - 但调用
store时并没有传入嵌套选择器参数,TS无法获知后续getNested会接收什么函数,只能将这类泛型 fallback 到约束类型unknown - 后续调用
getNested传入选择器时,外层store的泛型已经确定为unknown,不会再根据传入参数更新类型,最终导致返回值被推断为unknown
解决方案
核心调整思路:将仅在后续方法调用时才确定的类型,声明在对应方法的泛型中,而非外层函数的顶层泛型,去掉冗余的泛型占位参数,让TS在对应方法调用时实时完成类型推断。
修正后代码:
function store< T, Selector extends (a: T) => unknown >(state: T, selector: Selector) { // 提前提取第一个选择器的返回类型,复用类型定义 type SelectorReturn = ReturnType<Selector>; const get = () => selector(state) as SelectorReturn; // getNested 自身声明泛型,调用时才根据传入参数推断类型 const getNested = <NestedSelector extends (a: SelectorReturn) => unknown>( nestedSelector: NestedSelector ) => nestedSelector(selector(state) as SelectorReturn) as ReturnType<NestedSelector>; return { get, getNested, }; } const initialData = { foo: { bar: { id: 1, }, }, }; const myStore = store(initialData, (a) => a.foo.bar); const a = myStore.get(); // 自动推断为 { id: number } const result = myStore.getNested((state) => { return state.id; // state自动推断为{id: number},result自动推断为number });
调整要点:
- 外层
store仅保留初始化时就能确定的两个泛型:状态类型T、第一个选择器类型Selector,去掉所有和嵌套选择器相关的顶层泛型声明 - 用内部类型别名
SelectorReturn承接第一个选择器的返回类型,不需要额外声明泛型参数做类型占位 - 把
NestedSelector泛型声明移到getNested方法上,调用getNested传入选择器时,TS会实时推断选择器类型,自动推导返回值为ReturnType<NestedSelector>,不会再出现unknown的问题 - 修正后
getNested的入参state会自动获得第一个选择器返回值的类型提示,不需要额外手动指定类型
内容的提问来源于stack exchange,提问作者swennemen
相关产品推荐
相关产品推荐

