在 TypeScript 中将字符串转换为枚举
Casting strings to enum in TypeScript
在 TypeScript 中使用枚举时,我 运行 遇到了一个小问题。我的场景是这样的:
- 我定义了一个包含允许值的字符串枚举
- 我已经定义了一个接受任何传入值(
string
类型)的方法,并且必须将其转换为所述枚举
问题是,即使在检查方法中是否传入 value
之后,intellisense 告诉我 value
仍然是 string
类型而不是枚举类型。我如何强制 value
成为 AllowedValues
的类型?
这是一个概念验证示例:
/** enum */
enum AllowedValues {
LOREM_IPSUM = 'lorem ipsum',
DOLOR_SIT = 'dolor sir',
AMET = 'amet'
}
/** @method */
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
// If value is not found in enum, force it to a default
if (!(Object as any).values(AllowedValues).includes(value))
value = AllowedValues.LOREM_IPSUM;
// Value should be of type `AllowedValues` here
// But TypeScript/Intellisense still thinks it is `string`
console.log(value);
}
doSomething('amet'); // Should log `amet`
doSomething('aloha'); // Should log `lorem ipsum`, since it is not found in `AllowedValues`
您也可以在 TypeScript playground 上找到它。
这里发生了一些事情。一个是 TypeScript 不理解 Object.values(x).includes(y)
是 type guard on y
. It doesn't match the built-in ways that the compiler tries to narrow types, such as typeof
, instanceof
, or in
checks. To help the compiler out, you can use a user-defined type guard 来表达这种检查方式:
function isPropertyValue<T>(object: T, possibleValue: any): possibleValue is T[keyof T] {
return Object.values(object).includes(possibleValue);
}
declare function onlyAcceptAllowedValues(allowedValue: AllowedValues): void;
declare const v: string;
if (isPropertyValue(AllowedValues, v)) {
onlyAcceptAllowedValues(v); // v is narrowed to AllowedValues; it works!
}
所以让我们首先将您的函数更改为:
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
if (!(isPropertyValue(AllowedValues, value)))
value = AllowedValues.LOREM_IPSUM;
// TypeScript/Intellisense still thinks it is `string`
console.log(value);
}
呃,还是不行。
第二件事:如果您重新分配变量的值,TypeScript 基本上会放弃缩小范围。编译器付出了相当大的努力来理解控制流对变量类型的影响,但是 it's not perfect. So, even though we understand that assigning AllowedValues.LOREM_IPSUM
to value
makes it an AllowedValues
, the compiler gives up 并假定它是其原始注释类型,即 string
.
处理this的方法是创建一个编译器理解的新变量,它永远不会是AllowedValues
。最直接的方法是将其设为 const
变量,如下所示:
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
const allowedValue = isPropertyValue(AllowedValues, value) ? value : AllowedValues.LOREM_IPSUM;
console.log(allowedValue);
}
在上面,新变量 allowedValue
被推断为 AllowedValues
因为如果类型保护成功(此时 value
是 AllowedValues
),如果类型保护失败,则为 AllowedValues.LOREM_IPSUM
。无论哪种方式,allowedValue
都是 AllowedValues
.
因此,如果您想帮助编译器理解事物,那将是我建议的更改。希望有所帮助。祝你好运!
在 TypeScript 中使用枚举时,我 运行 遇到了一个小问题。我的场景是这样的:
- 我定义了一个包含允许值的字符串枚举
- 我已经定义了一个接受任何传入值(
string
类型)的方法,并且必须将其转换为所述枚举
问题是,即使在检查方法中是否传入 value
之后,intellisense 告诉我 value
仍然是 string
类型而不是枚举类型。我如何强制 value
成为 AllowedValues
的类型?
这是一个概念验证示例:
/** enum */
enum AllowedValues {
LOREM_IPSUM = 'lorem ipsum',
DOLOR_SIT = 'dolor sir',
AMET = 'amet'
}
/** @method */
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
// If value is not found in enum, force it to a default
if (!(Object as any).values(AllowedValues).includes(value))
value = AllowedValues.LOREM_IPSUM;
// Value should be of type `AllowedValues` here
// But TypeScript/Intellisense still thinks it is `string`
console.log(value);
}
doSomething('amet'); // Should log `amet`
doSomething('aloha'); // Should log `lorem ipsum`, since it is not found in `AllowedValues`
您也可以在 TypeScript playground 上找到它。
这里发生了一些事情。一个是 TypeScript 不理解 Object.values(x).includes(y)
是 type guard on y
. It doesn't match the built-in ways that the compiler tries to narrow types, such as typeof
, instanceof
, or in
checks. To help the compiler out, you can use a user-defined type guard 来表达这种检查方式:
function isPropertyValue<T>(object: T, possibleValue: any): possibleValue is T[keyof T] {
return Object.values(object).includes(possibleValue);
}
declare function onlyAcceptAllowedValues(allowedValue: AllowedValues): void;
declare const v: string;
if (isPropertyValue(AllowedValues, v)) {
onlyAcceptAllowedValues(v); // v is narrowed to AllowedValues; it works!
}
所以让我们首先将您的函数更改为:
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
if (!(isPropertyValue(AllowedValues, value)))
value = AllowedValues.LOREM_IPSUM;
// TypeScript/Intellisense still thinks it is `string`
console.log(value);
}
呃,还是不行。
第二件事:如果您重新分配变量的值,TypeScript 基本上会放弃缩小范围。编译器付出了相当大的努力来理解控制流对变量类型的影响,但是 it's not perfect. So, even though we understand that assigning AllowedValues.LOREM_IPSUM
to value
makes it an AllowedValues
, the compiler gives up 并假定它是其原始注释类型,即 string
.
处理this的方法是创建一个编译器理解的新变量,它永远不会是AllowedValues
。最直接的方法是将其设为 const
变量,如下所示:
function doSomething(value: string = AllowedValues.LOREM_IPSUM) {
const allowedValue = isPropertyValue(AllowedValues, value) ? value : AllowedValues.LOREM_IPSUM;
console.log(allowedValue);
}
在上面,新变量 allowedValue
被推断为 AllowedValues
因为如果类型保护成功(此时 value
是 AllowedValues
),如果类型保护失败,则为 AllowedValues.LOREM_IPSUM
。无论哪种方式,allowedValue
都是 AllowedValues
.
因此,如果您想帮助编译器理解事物,那将是我建议的更改。希望有所帮助。祝你好运!