在 TypeScript 中使用映射类型时更严格的联合类型
Stricter union types when using mapped types in TypeScript
在映射中使用联合类型时,我正在尝试使用映射类型来提供更多类型安全性。当使用 属性 类型(例如 ['value']
)作为键(K
)的类型时,似乎没有办法在 key/value 之间提供类型安全。
我想避免手动创建一个独特的模型来实现这一点。
代码:
interface IAction { value: string; }
type ActionMapper<A extends IAction> = {
[K in A['value']]: A;
}
interface IActionOne { value: 'action_one' }
interface IActionTwo { value: 'action_two' }
type Actions = IActionOne | IActionTwo;
const reducerMap: ActionMapper<Actions> = {
action_one: { value: 'action_one' },
action_two: { value: 'action_one' }, // expecting this line to fail
}
我已经评论了我预计会失败的行。
我觉得我应该能够利用键 (K in
) 提供正确的类型作为值。但是,我目前使用 A
,它提供了 IAction
实现,其中 value
的类型为 string
- 我想避免这种情况。
这在当前版本的 TypeScript 中可行吗?
是的,可以随心所欲。
正如您所注意到的,您的问题是 ActionMapper<A>
的属性值始终是 A
。对于 Actions
,这是联合类型。您真正想要做的是从 A
中提取与每个键 K
匹配 {value: K}
的成分。幸运的是,有一个 pre-defined conditional type named Extract
可以为您做到这一点。让我们重新定义 ActionMapper<A>
:
type ActionMapper<A extends IAction> = {
[K in A['value']]: Extract<A, {value: K}>;
}
现在我们再试一次:
const reducerMap: ActionMapper<Actions> = {
action_one: { value: 'action_one' },
action_two: { value: 'action_one' }, // error!
// Type '"action_one"' is not assignable to type '"action_two"'.
}
并且您得到了预期的错误。希望有所帮助。祝你好运!
在映射中使用联合类型时,我正在尝试使用映射类型来提供更多类型安全性。当使用 属性 类型(例如 ['value']
)作为键(K
)的类型时,似乎没有办法在 key/value 之间提供类型安全。
我想避免手动创建一个独特的模型来实现这一点。
代码:
interface IAction { value: string; }
type ActionMapper<A extends IAction> = {
[K in A['value']]: A;
}
interface IActionOne { value: 'action_one' }
interface IActionTwo { value: 'action_two' }
type Actions = IActionOne | IActionTwo;
const reducerMap: ActionMapper<Actions> = {
action_one: { value: 'action_one' },
action_two: { value: 'action_one' }, // expecting this line to fail
}
我已经评论了我预计会失败的行。
我觉得我应该能够利用键 (K in
) 提供正确的类型作为值。但是,我目前使用 A
,它提供了 IAction
实现,其中 value
的类型为 string
- 我想避免这种情况。
这在当前版本的 TypeScript 中可行吗?
是的,可以随心所欲。
正如您所注意到的,您的问题是 ActionMapper<A>
的属性值始终是 A
。对于 Actions
,这是联合类型。您真正想要做的是从 A
中提取与每个键 K
匹配 {value: K}
的成分。幸运的是,有一个 pre-defined conditional type named Extract
可以为您做到这一点。让我们重新定义 ActionMapper<A>
:
type ActionMapper<A extends IAction> = {
[K in A['value']]: Extract<A, {value: K}>;
}
现在我们再试一次:
const reducerMap: ActionMapper<Actions> = {
action_one: { value: 'action_one' },
action_two: { value: 'action_one' }, // error!
// Type '"action_one"' is not assignable to type '"action_two"'.
}
并且您得到了预期的错误。希望有所帮助。祝你好运!