Skip to content
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

5.0: const-like inference is not preserved unless a mapped type is used #53307

Closed
conorbrandon opened this issue Mar 17, 2023 · 3 comments Β· Fixed by #53341
Closed

5.0: const-like inference is not preserved unless a mapped type is used #53307

conorbrandon opened this issue Mar 17, 2023 · 3 comments Β· Fixed by #53341
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@conorbrandon
Copy link

Bug Report

πŸ”Ž Search Terms

const type, validator type

πŸ•— Version & Regression Information

5.0.2

  • I was unable to test this on prior versions because const type parameters are a new feature

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type NotEmpty<T extends Record<string, any>> = keyof T extends never ? never : T;
const thing = <const O extends Record<string, any>>(o: NotEmpty<O>) => {
  return o;
};
const t = thing({foo: ''});
// is inferred as { foo: string } ❌

// BUT, when adding a mapped type to NotEmpty it works
type NotEmptyMapped<T extends Record<string, any>> = keyof T extends never ? never : {
  [K in keyof T]: T[K] // ⬅️
};
const thingMapped = <const O extends Record<string, any>>(o: NotEmptyMapped<O>) => {
  return o;
};
const tMapped = thingMapped({foo: ''});
// is inferred as { foo: "" } βœ…

πŸ™ Actual behavior

When using a non-mapped validator type, the "valid" branch does not preserve the const-like inference unless the type is mapped over.

πŸ™‚ Expected behavior

A mapped type is not needed to preserve const-like inference.

@Andarist
Copy link
Contributor

Working case:

  • the apparent type of contextual type for this object is { [K in keyof O]: O[K]; }
  • thus the type for this property becomes O["foo"]
  • isConstTypeParameterContext is able to return true based on this information

Not working case:

  • the apparent type of contextual type for this object is Record<string, any>
  • thus the type for this property becomes any
  • isConstTypeParameterContext return false based on that

@conorbrandon
Copy link
Author

Interesting! Thanks for taking a look.

I did find it a bit curious that NotEmptyMapped unfortunately doesn't seem to actually error for an empty object, and instead returns Record<string, any>. Is that possibly related?

Both correctly error if the check is instead changed to {} extends T, but have the same const-like inference mismatch.

Finally if the check is T extends Record<string, never>, both don't use const-like inference and NotEmptyMapped doesn't error on an empty object, but NotEmpty does.

I think that's all the ways I can think of to check for an empty object...

@ahejlsberg ahejlsberg self-assigned this Mar 17, 2023
@ahejlsberg ahejlsberg added the Bug A bug in TypeScript label Mar 17, 2023
@ahejlsberg
Copy link
Member

It looks like we need to probe types a little deeper in isConstTypeVariable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants