Skip to content

Infer returns wrong type in 4.2.* #43161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Lehks opened this issue Mar 9, 2021 · 3 comments
Closed

Infer returns wrong type in 4.2.* #43161

Lehks opened this issue Mar 9, 2021 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Lehks
Copy link

Lehks commented Mar 9, 2021

Bug Report

With a particular type-setup, infer does not provide the correct type. When you open the playground link, you will see the type Opts and the types ExtractReq and ExtractOpt. Opts takes two type parameters, Required and Optional and ExtractReq and ExtractOpt will extract these two types using infer. However, the extracted types are, for the most part, incorrect.

🔎 Search Terms

infer, generic

🕗 Version & Regression Information

After updating TS to version 4.2.*. I have tried the nightly version and the problem did still arise.

  • This changed between versions 4.1.2 and 4.2.2

⏯ Playground Link

Playground link with relevant code

💻 Code

// Opts type:
type Opts<
    Required extends Record<string, unknown> | Never = Never,
    Optional extends Record<string, unknown> | Never = Never
> = Required & Partial<Optional>;

// extract types
type ExtractReq<T> = T extends Opts<infer Required, any> ? Required : never;
type ExtractOpt<T> = T extends Opts<any, infer Optional> ? Optional : never;

// shortand for Opts type with specific type parameters
type Opts3 = Opts<{ a: string }, { b: string }>;

// this type is Partial<{ a: string }> & Partial<{ b: string }> although { a: string } was expected.
type Req3 = ExtractReq<Opts3>;

// this type is Never | Record<string, unknown> although { b: string } was expected.
type Opt3 = ExtractOpt<Opts3>;

🙁 Actual behavior

The types that are inferred by ExtractReq and ExtractOpt are incorrect (see code above / playground link).

🙂 Expected behavior

The type { a: string } in the case of Req3 and { b: string } in the case of Opt3.

@RyanCavanaugh
Copy link
Member

Once you've mixed two types together into a new type, you can't "unmix" them this way unless some very particular conditions (i.e. ones you shouldn't rely on) are met. Our type alias preservation changes in 4.2 changed this example from a "happens to work" to a "happens to not work".

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Mar 9, 2021
@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@FlyingPumba
Copy link
Contributor

@RyanCavanaugh is there any other, more proper, way to "unmix" the types of a composed type as mentioned by OP.
E.g., from a type that is a Opts<T, Q>, obtain the particular T and Q.
I also had (important) code relying on this feature, and it stopped working after updating TS to 4.2.

It would be good to put a big red warning in TS documentation: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types
AFAIK, this is a listed feature, not just a "happens to work" feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants