简体   繁体   English

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

[英]Passing generic enum type as a parameter to a function in Typescript

I have two string enums that have the same variants, but have different string values.我有两个具有相同变体但具有不同字符串值的字符串枚举。

They get passed to the same initial function, which accepts the entire enum (which I think I can do using typeof , but each enum should have a different callback. What seems to be the best way to type the initial function?它们被传递给相同的初始函数,该函数接受整个枚举(我认为我可以使用typeof来做到这一点,但每个枚举应该有不同的回调。输入初始函数的最佳方法似乎是什么?

This code that I have doesn't work, as it complains that I can't use typeof with my T generic parameter.我拥有的这段代码不起作用,因为它抱怨我不能将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)

Your problem is that T is a type and not a value, so typeof T is a category error.你的问题是T是一个类型而不是一个值,所以typeof T是一个类别错误。 Your confusion probably stems from the fact that enum declarations bring into scope both a named value and a named type, and the names happen to be the same.您的困惑可能源于enum声明将命名值和命名类型都带入范围的事实,并且名称恰好相同。 But there's no general rule that a value and a type with the same name have any relationship to each other.但是没有一般的规则,同名的值和类型之间有任何关系。

For example, enum OpsA { /* ... */ } declares both the value OpsA (an object with property keys Start and Stop ) and the type OpsA (a union of the types of the property values of the enum object).例如, enum OpsA { /* ... */ }声明了值OpsA (具有属性键StartStop的对象)和类型OpsA (枚举对象的属性值类型的联合)。 When you write typeof OpsA as a type you are talking about the type of the enum object, while when you write OpsA as a type you are talking about the type of the enum object's property values.当您将typeof OpsA编写为类型时,您在谈论枚举对象的类型,而当您将OpsA编写为类型时,您正在谈论枚举对象的属性值的类型。

But the fact that the type OpsA is the property type of the object type typeof OpsA cannot be generalized the way you are presumably trying to do.但是,OpsA 类型是 OpsA 对象类型typeof OpsA OpsA属性类型这一事实不能以您可能试图做的方式概括。 There is no rule that says "given the type T there is a type typeof T whose properties are of type T ."没有规则说“给定类型T有一个类型typeof T其属性是类型T ”。 See an answer to a related question for more information.有关更多信息,请参阅相关问题的答案


Okay, if you can't write U extends typeof T , what can you do?好吧,如果你不能写U extends typeof T ,你能做什么? You want U to be an object type whose property keys are "Start" or "Stop" .您希望U成为其属性键为"Start""Stop"的对象类型。 Let's get a type for those keys first.让我们首先获取这些键的类型。 Since both ObjA and ObjB value have these keys, we can write that as either keyof typeof ObjA or keyof typeof ObjB :由于ObjAObjB值都有这些键,我们可以将其写为keyof typeof ObjAkeyof typeof ObjB

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

If we want to say that U should be an object type with keys of OpsKeys and values of type T , then we can use the Record<K, V> utility type : U extends Record<OpsKeys, T> .如果我们想说U应该是具有OpsKeys键和T类型值的对象类型,那么我们可以使用Record<K, V>实用程序类型U extends Record<OpsKeys, T> Like this:像这样:

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)
}

Now everything works as desired inside doSomething() .现在一切都在doSomething()中按预期工作。 Let's make sure that it also works for callers:让我们确保它也适用于调用者:

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

Looks good.看起来不错。 The compiler warns if you pass in a callback argument that's inappropriate for the action argument.如果您传入不适合action参数的callback参数,编译器会发出警告。

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM