将通用枚举类型作为参数传递给 Typescript 中的函数

Passing generic enum type as a parameter to a function in Typescript

我有两个具有相同变体但具有不同字符串值的字符串枚举。

它们被传递给相同的初始函数,该函数接受整个枚举(我认为我可以使用 typeof,但每个枚举都应该有不同的回调。这似乎是最好的方法输入初始函数?

我的这段代码不起作用,因为它抱怨我不能将 typeof 与我的 T 通用参数一起使用。

enum OpsA {
    Start = 'a_start',
    Stop = 'a_stop'
}

enum OpsB {
    Start = 'b_start',
    Stop = 'b_stop'
}

type EitherOps = OpsA | OpsB

function doSomething <T extends EitherOps, U extends typeof T>(action: U, callback: (a: T) => void) {
    console.log(action.Start)
    callback(action.Start)
    console.log(action.Stop)
    callback(action.Stop)
}

// does something special for OpsA
const callbackForA = (a: OpsA) => console.log(a)
// does something else special for OpsB
const callbackForB = (b: OpsB) => console.log(b)

doSomething(OpsA, callbackForA)
doSomething(OpsB, callbackForB)

您的问题是 T 是类型而不是值,因此 typeof T 是类别错误。您的困惑可能源于这样一个事实,即 enum 声明将命名值和命名类型都带入范围,并且名称恰好相同。但是,没有一般规则表明同名的值和类型之间有任何关系。

例如,enum OpsA { /* ... */ } 声明值 OpsA(具有 属性 键 StartStop 的对象)和类型 OpsA(枚举对象的 属性 值的 union 类型)。当您将 typeof OpsA 写为类型时,您是在谈论枚举对象的类型,而当您将 OpsA 写为类型时,您是在谈论枚举对象的 属性 值的类型.

但是类型 OpsA 是对象类型 typeof OpsA 的 属性 类型这一事实不能按照您可能尝试做的方式进行概括。没有规则说“给定类型 T 有一个类型 typeof T 其属性是类型 T”。有关详细信息,请参阅


好吧,如果你不会写 U extends typeof T,你会 做什么?您希望 U 是一个对象类型,其 属性 键是 "Start""Stop"。让我们先为这些键获取一个类型。由于 ObjAObjB 值都有这些键,我们可以将其写为 keyof typeof ObjAkeyof typeof ObjB:

type OpsKeys = keyof typeof OpsA;
// type OpsKeys = "Start" | "Stop"

如果我们想说U应该是一个对象类型,键为OpsKeys,值类型为T,那么我们可以使用the Record<K, V> utility typeU extends Record<OpsKeys, T>。像这样:

function doSomething<
  T extends EitherOps,
  U extends Record<OpsKeys, T>
>(action: U, callback: (a: T) => void) {
  console.log(action.Start)
  callback(action.Start)
  console.log(action.Stop)
  callback(action.Stop)
}

现在 doSomething() 内一切正常。让我们确保它也适用于来电者:

doSomething(OpsA, callbackForA) // okay
doSomething(OpsB, callbackForB) // okay
doSomething(OpsA, callbackForB) // error

看起来不错。如果您传入不适合 action 参数的 callback 参数,编译器会发出警告。

Playground link to code