Skip to content

Spread operator on a static factory method returns objects without instance methods and is uncaught by the compiler. #21472

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

Closed
ryandel1 opened this issue Jan 30, 2018 · 3 comments
Labels
Duplicate An existing issue was already created

Comments

@ryandel1
Copy link

TypeScript Version: 2.8.0-dev.20180127

Search Terms:
instance method spread static factory

Code

interface IGreeter {
    greeting: string
    greet(): string
}

class Greeter implements IGreeter {
    public constructor(public greeting: string) { }

    greet() { return this.greeting; }

    // Runtime error if greet() is called. Objects will not have a greet() method. (Unexpected)
    public static greeterFactory1(greeting: string): IGreeter {
        return {
            ...this.emptyGreeter(),
            greeting 
        }
    }

    // // Compile error. Objects will not have a greet() method.
    // public static greeterFactory2(greeting: string): IGreeter {
    //     return {
    //         ...new this(""),
    //         greeting 
    //     }
    // }

    public static emptyGreeter(): IGreeter {
        return new this("");
    }
}

Expected behavior:
I would expect the above code to not compile for the following reason:

Type '{ greeting: string; }' is not assignable to type 'IGreeter'.
  Property 'greet' is missing in type '{ greeting: string; }'.

This is the behavior when uncommented greeterFactory2.

Actual behavior:
Compilation succeeds and runtime exceptions will be thrown when greet() is called.

Playground Link: Playground Link

@aluanhaddad
Copy link
Contributor

The following are both valid implementations of IGreeter
Consider this

const emptyGreeter = {
  greet() {return this.greeting;},
  greeting: ''
};

and this

const emptyGreeter = new class {
  greet() {return this.greeting;}
  greeting = '';
}();

However, both interact differently with the spread operator.

There is not currently a way to express the ownness of a property at the type level and therefore no way to explicitly express the type level transformation implied by the spread operator.

#10727 tracks adding spread types.

However, the language does understand the distinction and tracks it correctly if you let it do its job.

The types of objects created by spreading other objects are in fact tracked and enforced.

However, by specifying the return type of emptyGreeter as IGreeter you have removed type information that the compiler would otherwise have, information it needs to track the effects of spreading an object. If you remove the type annotation, you will get the expected errors.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 30, 2018

Duplicate of #10288

@mhegazy mhegazy marked this as a duplicate of #10288 Jan 30, 2018
@mhegazy mhegazy added the Duplicate An existing issue was already created label Jan 30, 2018
@typescript-bot
Copy link
Collaborator

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

@microsoft microsoft locked and limited conversation to collaborators Jul 3, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants