question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Conditional type does not narrow union type

See original GitHub issue

TypeScript Version: 3.2.2

Search Terms: conditional types, unions, narrowing

Code

    interface A<T> {
        value: T;
    }

    interface Specification {
        [key: string]: Array<any> | Specification;
    }

    type Mapping<S extends Specification> = {
        [key in keyof S]: S[key] extends Array<infer T> ? A<T> : Mapping<S[key]>
        // Error                                                         ^^^^^^
        // Type 'S[key]' does not satisfy the constraint 'Specification'.
        //   Type 'Specification[key]' is not assignable to type 'Specification'.
        //     Type 'any[] | Specification' is not assignable to type 'Specification'.
        //       Type 'any[]' is not assignable to type 'Specification'.
        //         Index signature is missing in type 'any[]'.
    };

Expected behavior: No error. “Leafs” of the Specification tree, which have type Array<T> (for some T) should be mapped to A<T>, while non-leaf properties should be recursively mapped.

Actual behavior: In the right-hand side of the conditional type, S[key] is not narrowed to Specification, even if the complete type of S[key] is Array<any> | Specification and the Array<any> case is catched in the left-hand side.

Playground Link: link

Related Issues: some similar issues related to conditional types, but I’m not sure whether this is a duplicate of any of them.

Issue Analytics

  • State:open
  • Created 5 years ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
jack-williamscommented, Jan 3, 2019

Conditional types do not produce substitution types for the false branch of the conditional (a.k.a do not narrow in the false branch).

There was an attempted fix here: #24821, however this was closed.

0reactions
TotallyNotChasecommented, Jan 7, 2021

Alongside the narrowing mentioned here, Is it possible to add narrowing of union object types using in operator for conditional types?

E.g-

type Foo = string | { type: string };

type Bar<T extends string> = T;

type Qux<T extends Foo> = {
    [K in keyof T]: T[K] extends string ? Bar<T[K]> : T[K] extends { type: string } ? Bar<T[K]['type']> : never;
};

It’d be nicer if this was possible instead-

type Qux<T extends Foo> = {
    [K in keyof T]: 'type' in T[K] ? Bar<T[K]['type']> : Bar<T[K]>;
};
Read more comments on GitHub >

github_iconTop Results From Across the Web

Conditional type that narrows a union - Stack Overflow
so if your code has arrived at a narrower type you have to widen by assertion (whether explicitly or through a helper function)....
Read more >
Documentation - Narrowing - TypeScript
The “true” branch narrows x 's types which have either an optional or required property value , and the “false” branch narrows to...
Read more >
Narrowing Types in TypeScript - Formidable Labs
To narrow a union type down to one, we'll need to consider each case. We can do this with good old-fashioned control flow...
Read more >
The guide to conditional types in TypeScript - LogRocket Blog
TypeScript has support for conditional types, which might seem like ... Lastly, the distributive property doesn't hold if the union type is ...
Read more >
Advanced TypeScript: The Power and Limitations of ... - Medium
Okay, but that's not very specific. We can do much better by introducing the infer keyword. The infer keyword can go inside of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found