你的类型type EventFromType<Type extends Event["type"]> = (Event & { type: Type });最终表示为:
type EventFromType<Type extends EventType> = {
type: EventType.PostCreated & Type;
data: {
postId: number;
};
} | {
type: EventType.UserCreated & Type;
data: {
userId: number;
};
}
因此,当您将具体类型传递给 EventDataFromType 时,它会被评估为正确的结果。但是,当您通过 "data" 进行 generic 查找时,它不再依赖于 type 字段:
type EventDataFromType<Type extends EventType> = ({
type: EventType.PostCreated & Type;
data: {
postId: number;
};
} | {
type: EventType.UserCreated & Type;
data: {
userId: number;
};
})["data"]
// <=>
type EventDataFromType<Type extends EventType> = {
postId: number;
} | {
userId: number;
}
为了减轻这种行为,您应该对 EventFromType 进行惰性评估。仅在提供具体类型时才评估结果类型:
type EventFromType<Type extends Event["type"]> =
Type extends unknown ? (Event & { type: Type }) : never;
playground link
然后一切都按预期进行。
extends unknown 更新:
要了解它为什么会这样工作,让我们引入Id<T> 类型来热切地评估T 类型的字段:
type Id<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
首先让我们看看原来的EventFromType类型里面发生了什么:
type EventFromTypeEval<Type extends Event["type"]> = Id<Event & { type: Type }>
type EventFromTypeEval<Type extends EventType> = {
type: EventType.PostCreated & Type;
data: {
postId: number;
};
} | {
type: EventType.UserCreated & Type;
data: {
userId: number;
};
}
看起来该类型的结构中没有未知(双关语)类型,并立即得到热切评估。 Event 和 Type extends Event["type"] 拥有所有已知且数量非常有限的居民。这就解释了为什么通过data 字段进行通用 查找只会删除所有type 字段。
为了让它懒惰地评估,我们必须注入一些不确定性。
type EventFromTypeLazy<Type extends Event["type"]> =
Type extends unknown ? Event & { type: Type } : never
T extends unknown 是这里的理想人选。虽然任何类型 T 都是 unknown 的子集,并且会落入条件的真实分支,但它也可能是 union 类型,并且打字稿必须将其处理为 distributive conditional type . Typescript 不能简单地丢弃extends unknown。它必须等待具体类型,然后才评估结果类型。让我们看看Id<T>的结果内部:
type EventFromTypeLazyEval<Type extends Event["type"]> =
Id<Type extends unknown ? Event & { type: Type } : never>
type EventFromTypeLazyEval<Type extends EventType> = (Type extends unknown ? Event & {
type: Type;
} : never) extends infer O ? { [K in keyof O]: O[K]; } : never
playground link
这里没有急切的评价。 Typescript 保持类型不透明,直到提供具体的 Type。