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

A bug of type system? #18909

Closed
xiaocaibird opened this issue Oct 3, 2017 · 9 comments
Closed

A bug of type system? #18909

xiaocaibird opened this issue Oct 3, 2017 · 9 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@xiaocaibird
Copy link

xiaocaibird commented Oct 3, 2017

   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?

@ghost
Copy link

ghost commented Oct 3, 2017

It's not working for me for T1 either in typescript@next:

src/a.ts(47,42): error TS2344: Type 'obj1[k]' does not satisfy the constraint '{ [p in K]: obj1[k][p]; }'.
  Type '{ a1: string; a2: string; } | { b1: string; b2: string; }' is not assignable to type '{ [p in K]: obj1[k][p]; }'.
    Type '{ a1: string; a2: string; }' is not assignable to type '{ [p in K]: obj1[k][p]; }'.

I also don't see T2 working even if I delete a key from obj2.
I'm not sure if being able to compare those types is something we even support...

@xiaocaibird
Copy link
Author

it's working in typescript@2.5.2 @andy-ms

@ghost
Copy link

ghost commented Oct 3, 2017

I'm not sure what the correct behavior should be here... CC @sandersn

@xiaocaibird
Copy link
Author

image
@andy-ms

@xiaocaibird
Copy link
Author

xiaocaibird commented Oct 3, 2017

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

@andy-ms

@sandersn
Copy link
Member

sandersn commented Oct 3, 2017

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.

@sandersn
Copy link
Member

sandersn commented Oct 3, 2017

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

@sandersn sandersn added the Working as Intended The behavior described is the intended behavior; this is not a bug label Oct 3, 2017
@xiaocaibird
Copy link
Author

@sandersn Oh thank you for your answer, I'm still learning type system of typescript.

@mhegazy
Copy link
Contributor

mhegazy commented Oct 19, 2017

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

@mhegazy mhegazy closed this as completed Oct 19, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants