TypeScript icon indicating copy to clipboard operation
TypeScript copied to clipboard

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

Open Harpush opened this issue 1 year ago • 6 comments

🔎 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...

Harpush avatar Aug 24 '24 12:08 Harpush

Sort of a duplicate of https://github.com/microsoft/TypeScript/issues/48363 but with object types. It repros the same way when I wrap function arguments from that issue in objects: TS playground.

Andarist avatar Aug 24 '24 20:08 Andarist

Just adding that it also happened for unions

Harpush avatar Aug 25 '24 07:08 Harpush

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: https://github.com/microsoft/TypeScript/issues/59754

Andarist avatar Aug 26 '24 07:08 Andarist

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

RyanCavanaugh avatar Aug 26 '24 19:08 RyanCavanaugh

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

Andarist avatar Aug 26 '24 19:08 Andarist

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.

BalaM314 avatar Aug 27 '24 01:08 BalaM314

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

RyanCavanaugh avatar Aug 29 '24 16:08 RyanCavanaugh

https://www.typescriptlang.org/play/?#code/LAKALgngDgpgBASQHYDcCGAnAlmpYA8AKgHxwC8cAFAPoBchAlOaYQNyigDGA9kgM5g4AM27dycInBgAPMDCQATPnG4AjAFYxOYYpTXracRoeTpsuAgG8A2gAU4WJHADWMCNyFGAuidSYceER2XsQAvqRkpEgArgA2sQCE7CA8-IKqmOKSMnKKyjb2ji5uHt6+ZgEEuBBhugDuhsZGzHAx8UkcKbwCDkhCMBgYMAriGRiUlmiGItwTqoZgGNEwoQyryQD0G3A7u3v7AHoA-KBbcKk9Y4b4lqfbe1OIfuaBtyBn+7vzTxUW+KqiWIwXDETb3XbhMFwcJwAB08Lu526ghm1zeHy+hgB3CBuCh4URi2WDmUjjkGCgQzkIzQygARNjcUg6Z0tgBRaRQWJYThYMCxCBwPiwXlCQVgAAW8AA5vIBjy4FBMGgALZcZFSTnc3mCChjG6gB7lfx-N6fHbfUwmwKM4FIUGGiHEUDhCaPGZzBZLFZrBisODsgBKgYA8oHEXtjoiLijRGiI5i4ESYPjne8NkhuHUkxK0IJk60YMNlGAxKp4AzAXa6QAaByCLCkvADSkwalwWlwOnJlkgO4crk8vkCoUirBinMyuXYTiK5Uq4TcDBwMYdxSL7jqtKawc6gBMo0wBpARp+1qsjs+luelX+VZByT2BJArsm01Ex7231teJduks3zJqs6wBhsdRLs4fCshsA7asOgrClo47ilKcCykg8qzkqGCqouy6rrgIycLS+beh2yg-kgW49DIu58gAzIeGCfrsjxWi8F4nua16-Da972o+Tp-m676zABXrEp2lHAX6oHgRgkF3AAQvxlDJkwaCxHwYjyYpIBAA

BalaM314 avatar Aug 29 '24 17:08 BalaM314

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

Harpush avatar Sep 14 '24 16:09 Harpush