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

18 小时 41 分钟前
 jeremye

目前使用以下代码可以正常进行的类型推导,但每定义一个 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 }
  },
});
909 次点击
所在节点    TypeScript
12 条回复
bagel
18 小时 15 分钟前
Partial?
cheerxl
17 小时 24 分钟前
```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 }
},
});
```
li1218040201
17 小时 4 分钟前
问了下 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
16 小时 50 分钟前
```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
```
jeremye
16 小时 0 分钟前
@li1218040201 非常感谢,但是不太能理解 DefineConfig 中的 T[K] 传给 Options 后,T 为什么被推导为
```typescript
{
item1: {
id: number;
};
item2: {
name: string;
};
}
```
jeremye
15 小时 56 分钟前
@jeremye 问了 GPT 解释也是有点没懂..
xzyDeathGun
15 小时 51 分钟前
@jeremye 因为是错的,4 楼那个是对的
li1218040201
15 小时 35 分钟前
@jeremye 我有自己的理解,但解释不了,你的需求和小程序 Page({}) 传参,参数有各种提示类似,看看 miniprogram-type 包。也可以找找 vite 这种框架,有 defineConfig 方法,参考它们的类型声明。
jeremye
14 小时 19 分钟前
@xzyDeathGun 4 楼的那个返回值类型是正确的,但是传入 defineConfig 的参数不是正确的,在定义 config 时,params 的类型是 any ,可能是我的表达不够准确,初衷是帮助开发者在定义 config 时获得更友好的类型提示。
DOLLOR
14 小时 6 分钟前
function defineConfig<T>(config: {
[Property in keyof T]: {
a: () => T[Property]
b: (p: T[Property]) => void
}
}) {
return config
}

@jeremye 只能说 ts 过于牛逼,令人恐惧,居然能从 config 反推 T 的结构🤣
jeremye
9 小时 56 分钟前
@li1218040201 #8 之前只关注到了参数的类型,现在发现返回值类型也是 `{ item1: { id: number; }; item2: { name: string; }; }`, 似乎没办法保留参数原本的类型
li1218040201
47 分钟前
@jeremye 10 楼的方案我试了没问题的,你看下?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1094984

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX