Skip to content

Mapped Types Breakdown With Extends (extends / implements have inconsistent behavior, #25357

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
quebrooks opened this issue Jul 2, 2018 · 0 comments
Labels
Bug A bug in TypeScript
Milestone

Comments

@quebrooks
Copy link

TypeScript Version: 2.8.3, 2.9.1, 3.0.0-dev.20180630

Search Terms: mapped implements extends, extends does not exist

Code

// Take all keys in type T, add "x", and then make a new type out of this. Also, make
//    the "x" property have a different type.
type AddX<T> = {
    [key in (keyof T | "x")]: (
        key extends "x" ? number : string
    )
};

{
    interface Interface extends AddX<Interface> {

    }
    let object!: Interface;
    // test.ts:12:12 - error TS2339: Property 'x' does not exist on type 'Interface'.
    object.x;

    // test.ts:14:9 - error TS2322: Type 'Interface' is not assignable to type 'AddX<Interface>'.
    //    Property 'x' is missing in type 'Interface'.
    let explicitObject: AddX<Interface> = object;

    class InterfaceClass implements Interface {
        // Without this line a type error happens, so x is definitely required on any instance
        //    of Interface, but it also doesn't exist on variables with a type of Interface.
        x!: number;
    }
}

{
    class Class implements AddX<Class> {
        x!: number;
    }
    let object!: Class;
    object.x;

    let explicitObject: AddX<Class> = object;
}

Expected behavior:
No errors from either code section.

Actual behavior:
The interface code complains that property x does not exist on type 'Interface'. It also complains that Interface is not assignable to AddX, even though it extends that type.

Playground Link: https://www.typescriptlang.org/play/index.html#src=type%20AddX%3CT%3E%20%3D%20%7B%0D%0A%20%20%20%20%5Bkey%20in%20(keyof%20T%20%7C%20%22x%22)%5D%3A%20(%0D%0A%20%20%20%20%20%20%20%20key%20extends%20%22x%22%20%3F%20number%20%3A%20string%0D%0A%20%20%20%20)%0D%0A%7D%3B%0D%0A%0D%0A%7B%0D%0A%20%20%20%20interface%20Interface%20extends%20AddX%3CInterface%3E%20%7B%0D%0A%20%20%20%20%20%20%20%20%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20let%20object!%3A%20Interface%3B%0D%0A%20%20%20%20object.x%3B%0D%0A%0D%0A%20%20%20%20let%20explicitObject%3A%20AddX%3CInterface%3E%20%3D%20object%3B%0D%0A%0D%0A%20%20%20%20class%20InterfaceClass%20implements%20Interface%20%7B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20Without%20this%20line%20a%20type%20error%20happens%2C%20so%20x%20is%20definitely%20required%20on%20any%20instance%20of%20Interface%2C%20but%20it%20also%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20%20doesn't%20exist%20on%20variables%20with%20a%20type%20of%20Interface.%0D%0A%20%20%20%20%20%20%20%20x!%3A%20number%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0A%7B%0D%0A%20%20%20%20class%20Class%20implements%20Interface%20%7B%0D%0A%20%20%20%20%20%20%20%20x!%3A%20number%3B%0D%0A%20%20%20%20%7D%0D%0A%20%20%20%20let%20object!%3A%20Class%3B%0D%0A%20%20%20%20object.x%3B%0D%0A%0D%0A%20%20%20%20let%20explicitObject%3A%20AddX%3CClass%3E%20%3D%20object%3B%0D%0A%7D

Related Issues:
Possibly related to #21326, in both cases we are trying to combine constant properties with mapped types. But I have very little understanding of why my code doesn't work, so they might not be related at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

No branches or pull requests

3 participants