目前使用以下代码可以正常进行的类型推导,但每定义一个 item
都需要调用一次 defineItem
,想要实现一个 defineConfig
方法,省略掉 defineItem
,同时保留原先的类型推导。
interface Options<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 }
},
});
最终想要实现的效果如下,其中的 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
}
1
bagel 13 天前
Partial?
|
2
cheerxl 13 天前
```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 } }, }); ``` |
3
ltaoo1o 13 天前 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 } }, }, }); ``` |
4
muben 13 天前
```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[K]['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 ``` |
5
jeremye OP @li1218040201 非常感谢,但是不太能理解 DefineConfig 中的 T[K] 传给 Options 后,T 为什么被推导为
```typescript { item1: { id: number; }; item2: { name: string; }; } ``` |
7
xzyDeathGun 13 天前
@jeremye 因为是错的,4 楼那个是对的
|
8
ltaoo1o 13 天前
@jeremye 我有自己的理解,但解释不了,你的需求和小程序 Page({}) 传参,参数有各种提示类似,看看 miniprogram-type 包。也可以找找 vite 这种框架,有 defineConfig 方法,参考它们的类型声明。
|
9
jeremye OP @xzyDeathGun 4 楼的那个返回值类型是正确的,但是传入 defineConfig 的参数不是正确的,在定义 config 时,params 的类型是 any ,可能是我的表达不够准确,初衷是帮助开发者在定义 config 时获得更友好的类型提示。
|
10
DOLLOR 13 天前
function defineConfig<T>(config: {
[Property in keyof T]: { a: () => T[Property] b: (p: T[Property]) => void } }) { return config } @jeremye 只能说 ts 过于牛逼,令人恐惧,居然能从 config 反推 T 的结构🤣 |
11
jeremye OP @li1218040201 #8 之前只关注到了参数的类型,现在发现返回值类型也是 `{ item1: { id: number; }; item2: { name: string; }; }`, 似乎没办法保留参数原本的类型
|