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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:18:34