Skip to content

A bug of type system? #18909

Closed
Closed

Description

@xiaocaibird
   type base = {
        a: {
            a1: string,
            a2: string
        },
        b: {
            b1: string,
            b2: string
        }
        c: {
            c1: string,
            c2: string
        }
    }
    type key1 = keyof base;     //"a" | "b" | "c"
    type key2 = {[p in key1]: keyof base[p]}       //{ a: "a1" | "a2"; b: "b1" | "b2"; c: "c1" | "c2"; }



    type strictKeys<T extends {[p in K]: T[p]}, K extends string> = T;
    type obj1 = {
        a: {
            a1: string,
            a2: string
        },
        b: {
            b1: string,
            b2: string
        }
    }
    type obj2 = {
        a: {
            a1: string,
            a2: string
        },
        b: {
            b1: string,
            b2: string
        }
        c: {
            c1: string,
            c2: string
        }
    }

    type T1 = {
        [k in keyof obj1 & key1]: strictKeys<obj1[k], key2[k]>
    }   // is ok

    type T2 = {
        [k in keyof obj2 & key1]: strictKeys<obj2[k], key2[k]>
    }  // error

    type T3 = {
        a: strictKeys<obj2['a'], key2['a']>,
        b: strictKeys<obj2['b'], key2['b']>,        
        c: strictKeys<obj2['c'], key2['c']>,                
    }  // is ok

When i delete 'a' or 'b' or 'c' from 'obj2', 'T2' is ok.
Why?

Activity

  1. xiaocaibird commented on Oct 3, 2017

    @xiaocaibird
    Author

    it's working in typescript@2.5.2 Andy (Andrewkraft) (@Andy-MS)

  2. xiaocaibird commented on Oct 3, 2017

    @xiaocaibird
    Author
  3. xiaocaibird commented on Oct 3, 2017

    @xiaocaibird
    Author

    image
    when i delete 'c' from 'obj2', T2 is ok

    Andy (Andrewkraft) (@Andy-MS)

  4. sandersn commented on Oct 3, 2017

    @sandersn
    Member

    This is almost certainly due to the empty-intersection-elimination PR #18438. I'm constructing a smaller repro that's easier to understand so I can see whether the new behaviour is correct.

  5. sandersn commented on Oct 3, 2017

    @sandersn
    Member

    Briefly, strictKeys is wrong. Here's a couple of correct implementations. I'll explain our investigation below:

    type strictKeys<T extends Record<K, any>, K extends string> = T;
    // OR, even smaller:
    type strictKeys<T, K extends keyof T> = T;

    Circular constraints not reported

    type strictKeys<T extends { [P in K]: T[P] }, K extends string> = T;
                                          ~
                                          circular constraint for 'T'

    The underlying error is that strictKeys has a circular constraint, which is illegal and doesn't have well-defined semantics. However, this is not reported and the compiler does its best to produce some kind of type from instantiations of strictType.

    There seem to be other constructions that don't report circular constraints, such as index signatures.

    Impossible intersections break index access types

    In 2.5.2 and earlier, the first error is hidden by another limitation of the compiler: indexing a type with a union that includes impossible intersections just produces any:

    let good: Base['a' | 'b'] // ==> { a1, a2 } | { b1, b2 }
    let bad: Base['a' | 'b' | ('a' & 'b') | ('b' & 'a')] // ==> any

    #18438 now removes impossible intersections from unions, making bad now have the same type as good.

    It also means that which means that strictKeys<obj2[k], key2[k]> was previously strictKeys<any, key2[k]>. Now it's strictKeys<{ a1, a2 } | { b1, b2}, key2[k]>. This means that the first error is now visible.

    Improvements to impossible-intersection-removal

    Some recent change further improved impossible intersection removal. If you build from master, you'll see errors for both T1 and T2. That's because obj1[k] is now simplified to 'a' | 'b' just like obj2[k] is simplified to 'a' | 'b' | 'c'.

  6. xiaocaibird commented on Oct 4, 2017

    @xiaocaibird
    Author

    Nathan Shively-Sanders (@sandersn) Oh thank you for your answer, I'm still learning type system of typescript.

  7. mhegazy commented on Oct 19, 2017

    @mhegazy
    Contributor

    Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

  8. locked and limited conversation to collaborators on Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions