使用动态字符串为允许的枚举值键入保护?
Type guard for allowed enum value with dynamic string?
我正在处理来自第 3 方的一些数据,我想通过 ID 将其转换为地图,但前提是数据有效。我有一个允许属性的枚举,但无法弄清楚如何以编译器允许的方式检查数据是否有效。我试图检查在枚举上使用带有 in 运算符的 if 语句:
/** Allowed action values */
enum Actions {
Update = 'Update',
Delete = 'Delete'
}
/** Validated app events */
type AppEvent = {
id: string;
action: Actions;
}
/** 3rd party data I want to validate */
type RawEvent = {
id: string;
// ❗️I want to make sure this string is an allowed enum
action: string
}
type AppEventsById = {
[id: string]: AppEvent
}
const possiblyInvalidEvents: RawEvent[] = [
{
id: 'typescript',
action: 'Update'
},
{
id: 'uh oh',
action: 'oops'
}
]
const eventsById: AppEventsById = {}
possiblyInvalidEvents.forEach(event => {
// ❓Here I'm attempting to check that 3rd party action field is one of the allowed enums
if (event.action in Actions) {
eventsById[event.id] = {
id: event.id,
// Type 'string' is not assignable to type 'Actions'
action: event.action
}
}
})
// => I want eventsById to include the { id: 'typescript' } object, but not { id: 'uh oh' }
尝试分配给 action
引发此错误:Type 'string' is not assignable to type 'Actions'.
您只需要一个断言某个值 is Actions
的优化函数。任何基于此 return 值分支的代码都会记住您的值确实是您坚持的类型。因为你知道它是。
function isAction(input: string): input is Actions {
return input in Actions
}
要使用它,只需调用函数并在 return 为真时进行分支:
let someAction: Actions = Actions.Delete
const actionName: string = 'whatever'
if (isAction(actionName)) {
// Legal here, since we have verified the type.
someAction = actionName
}
您需要一个 user-defined type guard 函数来检查 string
是否是一个有效的 Actions
成员。这是一种明确告诉编译器应该使用某些 boolean
值表达式来缩小值类型的方法,如果它变成 true
。最简单的代码重构是这样的:
function isValidAction(str: string): str is Actions {
return str in Actions;
}
possiblyInvalidEvents.forEach(event => {
if (isValidAction(event.action)) {
eventsById[event.id] = {
id: event.id,
action: event.action // no error anymore
}
}
})
此 str in Actions
检查确实依赖于枚举的键和值相同的事实,这可能并不总是正确的。我可能会更愿意检查枚举的实际 values 而不是键,写出来有点令人讨厌,但至少不太可能突然中断:
function isValidAction(str: string): str is Actions {
return (Object.keys(Actions) as Array<keyof typeof Actions>).
some(k => Actions[k] === str);
}
但这取决于你。好的,希望有所帮助;祝你好运!
我正在处理来自第 3 方的一些数据,我想通过 ID 将其转换为地图,但前提是数据有效。我有一个允许属性的枚举,但无法弄清楚如何以编译器允许的方式检查数据是否有效。我试图检查在枚举上使用带有 in 运算符的 if 语句:
/** Allowed action values */
enum Actions {
Update = 'Update',
Delete = 'Delete'
}
/** Validated app events */
type AppEvent = {
id: string;
action: Actions;
}
/** 3rd party data I want to validate */
type RawEvent = {
id: string;
// ❗️I want to make sure this string is an allowed enum
action: string
}
type AppEventsById = {
[id: string]: AppEvent
}
const possiblyInvalidEvents: RawEvent[] = [
{
id: 'typescript',
action: 'Update'
},
{
id: 'uh oh',
action: 'oops'
}
]
const eventsById: AppEventsById = {}
possiblyInvalidEvents.forEach(event => {
// ❓Here I'm attempting to check that 3rd party action field is one of the allowed enums
if (event.action in Actions) {
eventsById[event.id] = {
id: event.id,
// Type 'string' is not assignable to type 'Actions'
action: event.action
}
}
})
// => I want eventsById to include the { id: 'typescript' } object, but not { id: 'uh oh' }
尝试分配给 action
引发此错误:Type 'string' is not assignable to type 'Actions'.
您只需要一个断言某个值 is Actions
的优化函数。任何基于此 return 值分支的代码都会记住您的值确实是您坚持的类型。因为你知道它是。
function isAction(input: string): input is Actions {
return input in Actions
}
要使用它,只需调用函数并在 return 为真时进行分支:
let someAction: Actions = Actions.Delete
const actionName: string = 'whatever'
if (isAction(actionName)) {
// Legal here, since we have verified the type.
someAction = actionName
}
您需要一个 user-defined type guard 函数来检查 string
是否是一个有效的 Actions
成员。这是一种明确告诉编译器应该使用某些 boolean
值表达式来缩小值类型的方法,如果它变成 true
。最简单的代码重构是这样的:
function isValidAction(str: string): str is Actions {
return str in Actions;
}
possiblyInvalidEvents.forEach(event => {
if (isValidAction(event.action)) {
eventsById[event.id] = {
id: event.id,
action: event.action // no error anymore
}
}
})
此 str in Actions
检查确实依赖于枚举的键和值相同的事实,这可能并不总是正确的。我可能会更愿意检查枚举的实际 values 而不是键,写出来有点令人讨厌,但至少不太可能突然中断:
function isValidAction(str: string): str is Actions {
return (Object.keys(Actions) as Array<keyof typeof Actions>).
some(k => Actions[k] === str);
}
但这取决于你。好的,希望有所帮助;祝你好运!