Typescript Exclude<> behaves differently in 3.8 and previous compared to newer versions

42 views Asked by At

I noticed a difference when using Exclude in Typescript in version 3.8 and previous compared to newer versions.

Consider the following example:

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",
};

The assignment of the object to const test would fail up until 3.8 and IMO rightfully complain that layerType does not exist on WithoutLayerType since it was excluded. In 3.9 and newer versions the assignment is fine though. It feels like a bug to me, but I'm sure that there is something to this that I do not understand :) Maybe someone can enlighten me please!

1

There are 1 answers

0
Daniel Gimenez On BEST ANSWER

The issue arises because how TypeScript changed it's handling of the type checking on intersection and optional properties in 3.9.

Prior to 3.9, the type result of LayeredCombined would be the combination of all properties. On and after 3.9, the result is never, because of the conflict between the types for layerType (one is LayerType.Image, the other is LayerType.Text).

Because LayeredCombined is now the type never, the type of the string literal from keyof Layercombined doesn't contain any properties from ImageLayer or TextLayer and therefore nothing can be excluded.

The easiest work around is redefining Layercombined as the following:

type Layercombined = Partial<ImageLayer> & Partial<TextLayer>;

This will get you all the keys because now layerType will have one possible value, undefined.