网站搜索

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。