英文:
Generic enum typeguard
问题
I want to write a generic type guard for enums but this doesn't work so far in TS 5.0.4
function isEnumValue<T>(enumType: T, value: unknown): value is T[keyof T] {
return Object.values(enumType).includes(value as T[keyof T]);
}
//-----------------------^--------------------------
// No overload matches this call
enum Sort {
A,
B
}
function isSort(value: unknown): value is Sort {
return isEnumValue(Sort, value);
}
enum Order {
ASC,
DESC
}
function isOrder(value: unknown): value is Order {
return isEnumValue(Order, value);
}
Maybe I can have a hint?
英文:
I want to write a generic type guard for enums but this doesn't work so far in TS 5.0.4
function isEnumValue<T>(enumType: T, value: unknown): value is T[keyof T] {
return Object.values(enumType).includes(value as T[keyof T]);
}
//-----------------------^--------------------------
// No overload matches this call
enum Sort {
A,
B
}
function isSort(value: unknown): value is Sort {
return isEnumValue(Sort, value);
}
enum Order {
ASC,
DESC
}
function isOrder(value: unknown): value is Order {
return isEnumValue(Order, value);
}
Maybe I can have a hint ?
答案1
得分: 1
以下是您要翻译的内容:
我将假设您只想解决“no overload matches this call”错误,并且函数的实现在运行时按您的意图工作... 即使数值枚举具有反向映射,其中 Object.values(enumType).includes(value)
在 value
是枚举 键 之一时会产生错误的正误。 有方法可以解决这个问题,但我考虑在此之外。
您遇到的问题是您的泛型类型参数 T
没有约束,因此可以是任何类型,包括其领域中带有 null
或 undefined
的类型。 因此,enumType
参数也可以是这种类型。
但是TypeScript库的类型定义用于the Object.values()
方法如下:
interface ObjectConstructor {
values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
values(o: {}): any[];
}
其中参数至少必须可分配给类型 {}
,这允许几乎任何东西,除了 null
和 undefined
。 有关更多信息,请参见 https://stackoverflow.com/q/59135229/2887218。
因此,上述两个Object.values()
的调用签名都不合适。 T
显然不受限于 {[s: string]: unknown} | ArrayLike<unknown>}
,这是第一个调用签名所需的,也不受限于 {}
,这是第二个调用签名所需的。
换句话说:因为没有什么能阻止某人调用
isEnumValue(null, "a");
编译器不想让您将 enumType
传递给 Object.values()
因此,修复此问题的方法是将 T
约束为非可为空的 {}
类型:
function isEnumValue<T extends {}>(enumType: T, value: unknown): value is T[keyof T] {
return Object.values(enumType).includes(value as T[keyof T]); // okay
}
这样可以工作,因为现在 isEnumValue()
不能使用可能为 null 的 enumType
参数调用。 如果您调用 isEnumValue(null, "a")
,将在调用站点得到编译器错误:
isEnumValue(null, "a"); // error
// Argument of type 'null' is not assignable to parameter of type '{}'.
看起来不错!
英文:
I'm going to assume that you just want to resolve the "no overload matches this call" error and that the implementation of the function works as you intend at runtime... even though numeric enums have reverse mappings where Object.values(enumType).includes(value)
will give a false positive when value
is one of the enum keys. There are ways to work around that but I'm considering this out of scope here.
The problem you're running into is that your generic type parameter T
is not constrained, and therefore could be any type at all, including those with null
or undefined
in their domain. And so the enumType
parameter can be of such a type.
But the TypeScript library typings for the Object.values()
method are like
interface ObjectConstructor {
values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
values(o: {}): any[];
}
where the parameter must at least be assignable to the type {}
, which allows just about anything except for null
and undefined
. See https://stackoverflow.com/q/59135229/2887218 for more information.
So neither of the two call signatures for Object.values()
shown above is appropriate. T
is certainly not constrained to {[s: string]: unknown} | ArrayLike<unknown>}
as needed for the first call signature, and it is not constrained to {}
as needed for the second call signature.
In other words: since nothing stops someone from calling
isEnumValue(null, "a");
the compiler doesn't want to let you pass enumType
into Object.values()
The fix for this is therefore to constrain T
to be the non-nullable {}
type:
function isEnumValue<T extends {}>(enumType: T, value: unknown): value is T[keyof T] {
return Object.values(enumType).includes(value as T[keyof T]); // okay
}
This works because now isEnumValue()
cannot be called with a possibly-nullish enumType
argument. If you call isEnumValue(null, "a")
you'll get a compiler error at the call site:
isEnumValue(null, "a"); // error
// Argument of type 'null' is not assignable to parameter of type '{}'.
Looks good!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论