gpt4 book ai didi

Typescripts 泛型,正在使用 keyof,但 TS 也使用符号 | 计算号码 |字符串也是

转载 作者:行者123 更新时间:2023-12-04 10:44:39 24 4
gpt4 key购买 nike

让我们直接进入示例。

在下面的代码中你可以看到 keyof正在使用,但 typescript 仍然计算 EVT 的类型如string|number|string , 即使 EVT扩展 keyof ((typeof Events)[OBJ])
代码:(在最后一行自动完成和类型检查工作正常)

export const Events = {
// [event: string]: (...args: any) => void;
account: {
available: (status?: 'online'|'offline',foo?:'test',bar?:'test2') => {
console.error('1');
},
}
}

export class AMQP {
static event<
OBJ extends keyof typeof Events,
EVT extends keyof ((typeof Events)[OBJ]),
FUNC extends ((typeof Events)[OBJ][EVT])
>(
obj: OBJ,
event: EVT,
...args: Parameters<FUNC>
) {

}
}

AMQP.event('account','available','online','test','test2');

Playground Link

问题(悬停在“有趣”上的红旗)
Type 'FUNC' does not satisfy the constraint '(...args: any) => any'.
Type '{ account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ][EVT]' is not assignable to type '(...args: any) => any'.
Type '{ account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ][keyof { account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ]]' is not assignable to type '(...args: any) => any'.
Type '{ account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ][string] | { account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ][number] | { ...; }[OBJ][symbol]' is not assignable to type '(...args: any) => any'.
Type '{ account: { available: (status?: "online" | "offline" | undefined, foo: "test", bar: "test2") => void; }; }[OBJ][string]' is not assignable to type '(...args: any) => any'.(2344)

更新:::

截至目前。 @Maciej Sikora 建议的问题,通过强制将 FUN 声明为函数修复错误。

但这是另一个例子。在没有强制声明的情况下可以正常工作
唯一的区别是事件不深,所以它在那里工作,但当对象更深一点时,我仍然很想知道原因。

Playground Link

最佳答案

按条件类型缩小类型

我们可以通过明确告诉 TS 我们只想要函数来修复它,即使目前只有看起来 TS 无法缩小它。考虑:

type E = typeof Events // for readability

export class AMQP {
static event<
OBJ extends keyof E,
EVT extends keyof E[OBJ],
FUNC extends E[OBJ][EVT] extends (...a:any[]) => any ? E[OBJ][EVT] : never
>(
obj: OBJ,
event: EVT,
...args: Parameters<FUNC>
) {

}
}

重点是- FUNC extends E[OBJ][EVT] extends (...a:any[]) => any ? E[OBJ][EVT] : never .这意味着我们只允许在对象结构的第二层中选择函数。现在看来 TS 不再提示了。

这种解决方案的另一个优点是,如果我们提供一个不是函数的字段,则不可能使用该选项调用该函数。

为什么 TS 不能缩小类型

有一些原因可以部分解释。考虑类似的例子:
const a = {
a: 2
}
const f = <A extends typeof a, K extends keyof A>(a: A, k: K) => {
return a[k] + 1; // error we cannot use +
}
f(a, 'a');

如果我们不能使用 + 会怎样?如果 a里面只有数字值。好像错了?不正确,因为我们的函数说我们允许 extends typeof a 的类型,所以它与 a 不同,我们可以拥有具有属性 a 的对象还有另一个属性 b这将是例如 bool 值。考虑下面的代码编译:
const b = {
a: 2,
b: true,
}
f(b, 'b');

所以我可以使用结构上具有相同字段的对象 - a但也有另一种不同类型的。出于同样的原因,您的代码不能缩小类型,因为我们可以引入另一个结构相同但具有另一个非函数属性的对象。

在我们的示例中,我们有一点不同的情况,因为我们指的是一个单一的对象。然后它不完全清楚为什么在某些嵌套级别上推理失败。 TS 需要更多帮助来缩小功能范围。为什么对我来说未知,看起来像是推理限制。

为事件引入静态类型

您应该考虑的第二个解决方案是静态输入事件接口(interface)。考虑:
type Events = Record<string, Record<string, (...a: any[]) => any>>
export const events = {
account: {
available: (status?: 'online' | 'offline', foo: 'test', bar: 'test2') => {
console.error('1');
},
}
};

export class AMQP {
static event<
INSTANCE extends Events,
OBJ extends keyof INSTANCE,
EVT extends keyof INSTANCE[OBJ],
FUNC extends INSTANCE[OBJ][EVT]
>(
inst: INSTANCE,
obj: OBJ,
event: EVT,
...args: Parameters<FUNC>
) {

}
}
// pay attention below I pass events object explicitly as first argument
AMQP.event(events, 'account', 'available', 'online', 'test', 'test2' );

请注意,我在这里介绍了几件事:
  • 事件类型
  • 附加的第一个参数是事件接口(interface)的实例对象

  • 这种解决方案更加灵活,因为实现不绑定(bind)到一个对象,您可以使用 Events 传递结构上正确的任何对象接口(interface),也不需要条件类型。

    关于Typescripts 泛型,正在使用 keyof,但 TS 也使用符号 | 计算号码 |字符串也是,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59765734/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com