请教一个 TS 问题,不知能否实现 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jeremye
V2EX    TypeScript

请教一个 TS 问题,不知能否实现

  •  
  •   jeremye 312 天前 3039 次点击
    这是一个创建于 312 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前使用以下代码可以正常进行的类型推导,但每定义一个 item 都需要调用一次 defineItem,想要实现一个 defineConfig 方法,省略掉 defineItem,同时保留原先的类型推导。

    interface Option<T> { a: () => T; b: (p: T) => void; } function defineItem<T>(options: Options<T>) { return options; } const cOnfig= { item1: defineItem({ a: () => ({ id: 1 }), b: (params) => {}, // params 类型是 { id: number } }), item2: defineItem({ a: () => ({ name: '1' }), b: (params) => {}, // params 类型是 { name: string } }), }; 

    预期的效果

    const cOnfig= defineConfig({ item1: { a: () => ({ id: 1 }), b: (params) => {}, // params 类型是 { id: number } }, item2: { a: () => ({ name: '1' }), b: (params) => {}, // params 类型是 { name: string } }, }); 
    第 1 条附言    312 天前

    最终想要实现的效果如下,其中的 method1 可以动态,并且可以再定义 method2、method3...,c 的参数和返回值分别约束为 a 与 b 的返回值,defineModule 的返回值类型就保留原本的结构,如果这样设计不能实现的话,有没有更好的方式来设计这个 API 呢?

    type Module<T, K> = { method1: { a: () => T b: () => K c: (params: T) => K } } const module = defineModule({ method1: { a: () => ({ id: 1 }), b: () => ({ id: 1, name: 'test' }), // c: (params: { id: number; }) => { id: number; name: string; }; } c: (params) => {} } }) /* module: { method1: { a: () => { id: number; }; b: () => { id: number; name: string; }; c: (params: { id: number; }) => { id: number; name: string; }; } } */ function defineModule<T, K>(module: Module<T, K>) { return module } 
    12 条回复    2024-12-05 10:07:34 +08:00
    bagel
        1
    bagel  
       312 天前
    Partial?
    cheerxl
        2
    cheerxl  
       312 天前
    ```typescript
    function defineConfig<T>(options: Record<string, Options<T>>) {
    return options;
    }

    const cOnfig= defineConfig<Record<string, any>>({
    item1: {
    a: () => ({ id: 1 }),
    b: (params) => {}, // params 类型是 { id: number }
    },
    item2: {
    a: () => ({ name: '1' }),
    b: (params) => {}, // params 类型是 { name: string }
    },
    });
    ```
    ltaoo1o
        3
    ltaoo1o  
       312 天前   1
    问了下 GPT ,稍微调整了下

    ```
    interface Options<T> {
    a: () => T;
    b: (p: T) => void;
    }

    type DefineConfig<T> = {
    [K in keyof T]: Options<T[K]>;
    };

    function defineConfig<T extends Record<string, any>>(config: DefineConfig<T>): T {
    return config;
    }

    // 使用 defineConfig 定义配置对象
    const cOnfig= defineConfig({
    item1: {
    a: () => ({ id: 1 }),
    b: (params) => {
    console.log(params.id); // params 类型是 { id: number }
    },
    },
    item2: {
    a: () => ({ name: '1' }),
    b: (params) => {
    console.log(params.name);
    // params 类型是 { name: string }
    },
    },
    });

    ```
    muben
        4
    muben  
       312 天前
    ```ts
    interface Options<T extends any> {
    a: () => T;
    b: (p: T) => void;
    }

    type ConfigType<T extends Record<string, Options<any>>> = {
    [K in keyof T]: {
    a: () => T[K]['a'];
    b: (p: ReturnType<T[]['a']>) => void;
    };
    };

    function defineConfig<T extends Record<string, Options<any>>>(options: T): ConfigType<T> {
    return options;
    }

    const cOnfig= defineConfig({
    item1: {
    a: () => ({ id: 1 }),
    b: (params) => {}, // params 类型是 { id: number }
    },
    item2: {
    a: () => ({ name: '1' }),
    b: (params) => {}, // params 类型是 { name: string }
    },
    });

    config.item1.b({ id: 1 }); // ok
    config.item1.b({ name: '1' }); // error

    config.item2.b({ id: 1 }); // error
    config.item2.b({ name: '1' }); // ok
    ```
    jeremye
        5
    jeremye  
    OP
       312 天前
    @li1218040201 非常感谢,但是不太能理解 DefineConfig 中的 T[K] 传给 Options 后,T 为什么被推导为
    ```typescript
    {
    item1: {
    id: number;
    };
    item2: {
    name: string;
    };
    }
    ```
    jeremye
        6
    jeremye  
    OP
       312 天前
    @jeremye 问了 GPT 解释也是有点没懂..
    xzyDeathGun
        7
    xzyDeathGun  
       312 天前
    @jeremye 因为是错的,4 楼那个是对的
    ltaoo1o
        8
    ltaoo1o  
       312 天前
    @jeremye 我有自己的理解,但解释不了,你的需求和小程序 Page({}) 传参,参数有各种提示类似,看看 miniprogram-type 包。也可以找找 vite 这种框架,有 defineConfig 方法,参考它们的类型声明。
    jeremye
        9
    jeremye  
    OP
       312 天前
    @xzyDeathGun 4 楼的那个返回值类型是正确的,但是传入 defineConfig 的参数不是正确的,在定义 config 时,params 的类型是 any ,可能是我的表达不够准确,初衷是帮助开发者在定义 config 时获得更友好的类型提示。
    AV1
        10
    AV1  
       312 天前
    function defineConfig<T>(config: {
    [Property in keyof T]: {
    a: () => T[Property]
    b: (p: T[Property]) => void
    }
    }) {
    return config
    }

    @jeremye 只能说 ts 过于牛逼,令人恐惧,居然能从 config 反推 T 的结构
    jeremye
        11
    jeremye  
    OP
       312 天前
    @li1218040201 #8 之前只关注到了参数的类型,现在发现返回值类型也是 `{ item1: { id: number; }; item2: { name: string; }; }`, 似乎没办法保留参数原本的类型
    ltaoo1o
        12
    ltaoo1o  
       312 天前
    @jeremye 10 楼的方案我试了没问题的,你看下?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5899 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 03:19 PVG 11:19 LAX 20:19 JFK 23:19
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86