Skip to content

Conditional type inference regression in 4.2*Β #43192

Closed
@robertf224

Description

@robertf224

Bug Report

πŸ”Ž Search Terms

"conditional type inference"

πŸ•— Version & Regression Information

This changed between versions 4.1.5 and 4.2.2

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

export interface IFruits {
    tropical: {
        papaya: number;
        pineapple: number;
        guava: number;
    };

    stone: {
        peach: number;
        nectarine: number;
    };

    berry: {
        strawberry: number;
        blueberry: number;
    };
}

/**
 * Aggregating all the second level keys (the fruits) into a single union type.
 */
export type FruitKey = {
    [category in keyof IFruits]: keyof IFruits[category];
}[keyof IFruits];

/**
 * Helper to pick only fruit categories (first level) that are required for the requested fruit keys.
 */
type RequiredFruitCategories<K extends FruitKey> = {
    [category in keyof IFruits]: Extract<K, keyof IFruits[category]> extends never ? never : category;
}[keyof IFruits];


/**
 * Takes a nested slice of the IFruits interface depending on the fruit keys (second level) that
 * we ask for -- essentially a nested `Pick`.  So `FruitsSubset<"papaya" | "peach">` ends up being:
 * {
 *      tropical: {
 *          papaya: number;
 *      };
 *      stone: {
 *          peach: number;
 *      };
 * }
 */
type FruitsSubset<K extends FruitKey> = {
    [category in RequiredFruitCategories<K>]: Pick<
        IFruits[category],
        Extract<K, keyof IFruits[category]>
    >;
};

/**
 * Just wrapping the above in an interface for other interfaces (i.e. React props) to extend.
 */
interface FruitsSubsetOpts<K extends FruitKey> {
    fruits: FruitsSubset<K>;
}

type MyFavoriteFruitsSubset = FruitsSubset<"papaya" | "peach">;
// Inferring the key from above -- this ends up being `"papaya" | "peach"` on TS 4.1.5 but `never` on 4.2.3
type MyFavoriteFruitsSubsetKey = MyFavoriteFruitsSubset extends FruitsSubset<infer K> ? K : never;

type MyFavoriteFruitsSubsetOpts = FruitsSubsetOpts<"papaya" | "peach">;
// Inferring the key from above -- this ends up being `"papaya" | "peach"` on both TS 4.1.5 and 4.2.3
// Pretty confusing because FruitsSubsetOpts is just a wrapper around FruitsSubset!
type MyFavoriteFruitsSubsetOptsKey = MyFavoriteFruitsSubsetOpts extends FruitsSubsetOpts<infer K> ? K : never;

πŸ™ Actual behavior

MyFavoriteFruitsSubsetKey can be properly inferred on TS 4.1.5 but not on 4.2.3 (we tried migrating to 4.2.2 locally and this broke so the change in behavior is somewhere between 4.1.5 and 4.2.2)

πŸ™‚ Expected behavior

MyFavoriteFruitsSubsetKey should be properly inferred on both versions. Could be we're doing a thing we shouldn't be doing in the first place, but it's strange that it only kind of breaks and doesn't completely break.

Metadata

Metadata

Assignees

Labels

Breaking ChangeWould introduce errors in existing codeWorking as IntendedThe behavior described is the intended behavior; this is not a bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions