TypeScript 的 Exclude<> 在 3.8 及以前版本与较新版本中的行为有所不同
我注意到在 TypeScript 中使用 Exclude 时,3.8 及以前版本与较新版本相比有差异。
考虑以下示例:
interface Layer {
id: string;
}
export enum LayerType {
Text,
Image,
}
interface ImageLayer extends Layer {
src: string,
layerType: LayerType.Image;
}
interface TextLayer extends Layer {
text: string,
layerType: LayerType.Text;
}
type Layercombined = ImageLayer & TextLayer;
type IgnoredProperties = "layerType";
type WithoutLayerType = {
[key in Exclude<keyof Layercombined, IgnoredProperties>]: string;
};
const test: WithoutLayerType = {
id: "17",
src: "test",
text: "test",
layerType: "type",
};
将对象分配给 const test 在 3.8 及之前的版本中会失败,并且在我看来会合理地抱怨 WithoutLayerType 上不存在 layerType,因为它被排除了。不过在 3.9 及以后的版本中,该分配是可以的。对我来说这感觉像是一个 bug,但我确信这里有我不理解的事情 :) 希望有人能给我解释一下!
答案#1
这个问题的产生是因为 TypeScript 在 3.9 中改变了它对交叉类型和可选属性的类型检查处理方式。
在 3.9 之前,LayeredCombined 的类型结果将是所有属性的组合。而在 3.9 及之后,由于 layerType 的类型冲突(一个是 LayerType.Image,另一个是 LayerType.Text),结果永远为 never。
因为 LayeredCombined 现在的类型是 never,所以来自 keyof Layercombined 的字符串字面量类型不包含来自 ImageLayer 或 TextLayer 的任何属性,因此无法排除任何内容。
最简单的解决方法是将 Layercombined 重新定义为如下内容:
type Layercombined = Partial<ImageLayer> & Partial<TextLayer>;
这将为你获取所有的键,因为现在 layerType 将只有一个可能的值,undefined。
