typescript - 泛型函数中的打字稿范围不能按预期缩小
问题描述
泛型函数中的打字稿范围不能按预期缩小
我有一个EventType
枚举,EventType
以及与其对应的参数之间的关系。
enum EventType { A, B };
interface EventParams {
[EventType.A]: { name: string, age: number }
[EventType.B]: { address: string }
}
EventType
但是当我在泛型函数中使用这些定义并通过if
范围阐明的值时,param
并没有缩小到相应的类型EventParams
:
const run1 = function <T extends EventType>(eventName: T, param: EventParams[T]) {
if (eventName === EventType.A) {
// ts error here
param.name
}
}
它仍然看起来像是 中所有值的集合EventParams
:
这是另一个相关的问题:当我为每个创建处理回调时EventType
:
type EventCallbacks = {
[EventName in EventType]: (param: EventParams[EventName]) => any
}
const callbacks: EventCallbacks = {
[EventType.A]: param => {},
[EventType.B]: param => {}
}
而在泛型函数中使用这些, 的类型callback
依然没有缩小到对应的T
泛型:
const run2 = function <T extends EventType>(eventName: T, param: EventParams[T]) {
const callback = callbacks[eventName];
// ts error here
callback(param);
}
这是 Playground 中的完整示例。
那么我的代码是否有问题或打字稿无法处理这种类型的缩小?
解决方案
想象一下这个场景
run1<EventType.A | EventType.B>(EventType.A, {address: 'foo'})
这是完全有效的代码。如果T
extends EventType
,这并不意味着它T
是 of 之一EventType
,它完全可以是一个 union。在这种情况下,您可以传递EventType.B
to和toeventName
的对象,这会在运行时导致问题。所以你不能这样做。一种选择是使用类型保护,尽管我认为使用这样的有区别的联合会更好:EventType.A
param
const run1 = function<T extends EventType>(
data: T extends any ? {eventName: T, param: EventParams[T]} : never
) {
if (data.eventName === EventType.A) {
// no ts error here
data.param.name
}
}
现在 if T
is EventType.A | EventType.B
,data
将是类型
| {eventName: EventType.A, param: EventParams[EventType.A]}
| {eventName: EventType.B, param: EventParams[EventType.B]}
打字稿能够推断出一切。确保不要破坏参数(不要这样做const {eventName, param} = data
),因为在进行解构时,可区分联合中的连接会丢失。通常你可以if
在类型缩小之后解构语句中的变量,但在这种情况下 ts 会有点混乱并且不能按预期工作,可能是因为泛型。
您还需要这个T extends any
东西,以便如果T
是联合,则为联合的每个成员创建一个单独的对象。如果你忽略它,你只会得到{eventName: EventType, param: EventParams[EventType]}
什么不好。
实际上,这样一来,您实际上并不需要泛型,您可以像这样为事件/参数对的所有组合创建一个可区分的联合
const run2 = function(
data: {
[key in EventType]: {eventName: key, param: EventParams[key]}
}[EventType]
) {
if (data.eventName === EventType.A) {
// Now typescript doesn't get confused when destructuring
const {eventName, param} = data
param.name
}
}
回调也一样,如果T
是联合,打字稿无法推断出正确的类型。这也可以使用有区别的联合来解决,请参阅Sandbox。但是,如果您只想const callback = callbacks[eventName]
不检查eventName
类型,它会有点复杂,您可能需要一些类型转换。
推荐阅读
- python - 在列表中的每个项目(字符串)之前添加整数
- java - 如何将 Spring 属性文件注入 JUnit 测试?
- python - 如何允许用户在 Python 中使用 while 循环输入列表列表
- html - 字体真棒图标随着窗口调整大小在输入字段中移动
- node.js - WebSocket 握手期间的 Socket.io 错误:意外的响应代码:500 windows server iis
- javascript - 为什么在 reactjs 中使用 JSON.parse 会导致跨源错误?
- sql - 在 SQL 查询中,我无法获得真正的数据
- python - 尽管已安装 Python 无法导入模块
- json - 如何访问从 Flutter 中的 HTTP POST 请求获得的 JSON 响应?
- java - 当您不知道循环中将输入多少个变量时,如何分配多个变量?