Skip to content

Boolean narrowed to true only when generic type is explicitly provided while it shouldn't #59738

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

Open
Harpush opened this issue Aug 24, 2024 · 9 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@Harpush
Copy link

Harpush commented Aug 24, 2024

πŸ”Ž Search Terms

specify type narrow union

πŸ•— Version & Regression Information

All versions I checked and nothing in FAQ that seems related

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/LAKA9GCqDOCmAEAXAFggbgQwE4EsMDsBjBA-Ae0Q0RzPyTPgDMytilkdokBPABwUQMARghz5MuAogA08WGlh0UZAK4BzZPByJ4Ad1UAbACbwKqLLs4IR8QmQl58iUIj4IAkuOyPEAHjGmKjoAKgB88AC88AAUAJSR4cEA3KCgdvjQOswMUb7BcgAeiIpGXGRCAFawhIih0eUVAFzwwbHNng5SvgDeANoAClp0ANaw3GSMLQC67V6STnkDU6EAvuER4fgqBgYAhCkg6ZnwQtiR8HmFxfil8H2DAaPjk8Ez8B3eXQTcq3W6za0AQl4FsdvtUodaMcxIxYFgsLATFFTlhot0MM1smihM1EFgVLAVrEiQcIPByRTKVSAHoAflAZKOOhRzR6DLAVIx7zmPjZ4A5VIpOO5nQWQjIZAMsAIoVJAopazl8DW8AAdOr2bYoVkJazuprKcLxZLpfglWtNXiCVouGJilheAjiiYMFwAETGqUEN0QiAAUQKvAMOEI2gM3Hg0H4ocYEZQCDUijhIfgvGwGAAtmltYUgyHtOcUXzKVyPvM-PqQILycKy7zPabZaBKRaQGs0Vysd1hVbCcTYkl4P6AErDgDyw4NFLpmqZTF1F0rZMNuPxsHNoXZ5F07CoSDXINgiK4ghOCA9Eq9+DdsgLnCG9sdsGd8Fd8DdvZ9IHZAbzocQ4aRtGOCxuwCZJrghCpumGbzlgJxnAQJjZNmGQ6LAgbBv+ABMhbYMWFKljyXSVtWJyzKKfgNjKBwtpubZ1OimISgRNbNNRZqgO23argSRIkkOYD6FgwzQL6YC-lhYYRlG1QgXGqDwIm+DJlBaZYJmcEIfBSG2K6Oi9q+XAcahxwYX+2gAMx4VgrGvhRnwLKR1a1sRYqXo2tEKvR3GdhK2K8SQxkeQQ-EDoJwmieyABCIX4NEvbxBgBjQAwkViSAQA

πŸ’» Code

// Use the variance annotation to force this type to be invariant, even though it would otherwise be covariant
type Invariant<in out T> = () => T;

const foo = <T extends object>(obj: T): Invariant<{[P in keyof T]: Invariant<T[P]>}> => null!;
const bar = <T extends {[P in keyof T]: Invariant<any>}>(w: T): T => null!;

const inferred = bar({a: foo({b: true})}); // true is boolean as expected

// Explicitly specify the generic param
const explicit = bar<{
    a: Invariant<{
        b: Invariant<boolean>;
    }>
}>({a: foo({b: true})}); // narrowed to true instead of boolean and errors

πŸ™ Actual behavior

Narrows to true

πŸ™‚ Expected behavior

Should stay boolean

Additional information about the issue

Doing as boolean works...

@Andarist
Copy link
Contributor

Sort of a duplicate of #48363 but with object types. It repros the same way when I wrap function arguments from that issue in objects: TS playground.

@Harpush
Copy link
Author

Harpush commented Aug 25, 2024

Just adding that it also happened for unions

@Andarist
Copy link
Contributor

Andarist commented Aug 26, 2024

Well, boolean is a union of true | false so it makes sense that unions are affected πŸ˜‰ It's just that boolean is expected to behave more like a primitive than a union - hence the issue I linked.

The union case can be reproduced easily using the same kind of code (TS playground):

type Box<T> = {
  get: () => T;
  set: (value: T) => void;
};

declare function box<T>(value: T): Box<T>;

const bb1: Box<"foo" | "bar"> = box("foo"); // errors today
const bb2: Box<{ prop: "foo" | "bar" }> = box({ prop: "foo" }); // errors today

The algorithm can only keep the literal type based on the contextual type. There is no mechanism to widen it to the contextual union type when that position refers to an invariant type parameter. I think this issue here should stay focused on the invariant problem and I'll create a new one for this specific issue with boolean: #59754

@RyanCavanaugh
Copy link
Member

What's a repro that doesn't depend on an incorrect variance annotation?

@Andarist
Copy link
Contributor

@RyanCavanaugh I think it's the one I posted above, the one with "foo" | "bar".

@BalaM314
Copy link

BalaM314 commented Aug 27, 2024

What's a repro that doesn't depend on an incorrect variance annotation?

Replace

//Use the variance annotation to force this type to be invariant, even though it would otherwise be covariant
type Invariant<in out T> = () => T;

with

type Invariant = (_:T) => T;

and the error is the same.

@RyanCavanaugh
Copy link
Member

I don't see an error after removing the variance annotation

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Aug 29, 2024
@Harpush
Copy link
Author

Harpush commented Sep 14, 2024

@RyanCavanaugh anything else needed here? It is still marked with needs more info

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

4 participants