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

Issues with extending ObjectConstructor interface #3889

Closed
kitsonk opened this issue Jul 16, 2015 · 11 comments
Closed

Issues with extending ObjectConstructor interface #3889

kitsonk opened this issue Jul 16, 2015 · 11 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@kitsonk
Copy link
Contributor

kitsonk commented Jul 16, 2015

While this currently doesn't error in Playground, in 1.5.0-beta and release-1.5 branch, I get error TS2339: Property 'assign' does not exist on type 'ObjectConstructor' for the following:

interface ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}

let assign = Object.assign ? Object.assign : function(target: any, ...sources: any[]): any {
    return;
};
@qc00
Copy link

qc00 commented Jul 16, 2015

This is due to some limitations in definition merging where interfaces declared in d.ts files do not merge with "normal" ones declared in .ts code.

Your example would have worked if you could say

declare interface ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}

but you can't do declare on interface.

The only solution is to move the interface bit into its own .d.ts and ///<referece it from your code.

@mhegazy
Copy link
Contributor

mhegazy commented Jul 16, 2015

@billccn interfaces always merge, even accross files. there is no such limitation. looks like you are talking about interfaces defined within a module, since modules have thier own scope, and interface with the same name as one in the global scope will not merge..

e.g.:

// global.d.ts
interface I {
    a: string
}


// myModule.ts
import d from "anotherModule";

// This I is local to myModule and does not merge with the global"
interface I {
    b: number;
}

@mhegazy
Copy link
Contributor

mhegazy commented Jul 16, 2015

@kitsonk the sample you provided works fine for me on latest. can you provide more details?

@mhegazy mhegazy added the Needs More Info The issue still hasn't been fully clarified label Jul 16, 2015
@kitsonk
Copy link
Contributor Author

kitsonk commented Jul 16, 2015

Ok, I tried several different variations because I got it "working" accidentally and I realise the problem. Now I don't know if it is as designed or not (it is very confusing from a developer though).

The following works:

interface ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}

let assign = Object.assign ? Object.assign : function(target: any, ...sources: any[]): any {};

function test () {};

The following gives Property 'assign' does not exist on type 'ObjectConstructor':

interface ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}

let assign = Object.assign ? Object.assign : function(target: any, ...sources: any[]): any {};

export function test () {};

Obviously it has to do with modules and namespacing, but I will admit I am confused.

@RyanCavanaugh
Copy link
Member

When you have a top-level export, your file is a module, which means all declarations are introduced at the module scope rather than the global scope. This means that interface ObjectConstructor declares a new type ObjectConstructor (at the module scope) rather than merging with the ObjectConstructor type from the global scope.

This is intentional because otherwise there wouldn't be any way to make a new interface local to your module that had the same name as a global type.

@kitsonk
Copy link
Contributor Author

kitsonk commented Jul 16, 2015

Thanks. I figured it was intentional, but obviously it is rather confusing, especially when not having a "choice" about accessing globally scoped namespaces at the implementation level. I assume there are no language semantics to denote that I am referring to the global namespace? Therefore, the only way to extend it is as @billccn suggests (and @mhegazy implies) by declaring in a non-module, like a project .d.ts.

@RyanCavanaugh
Copy link
Member

Correct. Some related discussion in #3857 and #983

@bryanforbes
Copy link
Contributor

@kitsonk Given what @RyanCavanaugh explained, could you do something like this?

interface ObjectCtor extends ObjectConstructor {
    assign(target: any, ...sources: any[]): any;
}
declare var Object: ObjectCtor;
export let assign = Object.assign ? Object.assign : function(target: any, ...sources: any[]): any {
        return;
};

@kitsonk
Copy link
Contributor Author

kitsonk commented Jul 16, 2015

@bryanforbes thanks for the effective workaround. #983 being re-opened would also address the "limitation" I am perceiving so I will close in lieu of that.

@tomitrescak
Copy link

tomitrescak commented Aug 23, 2016

Guys, sorry to comment in this zombie thread but there is weird stuff happening.
Declaring a following definition file does nothing and typescript does not recognise the polyfill:

// file polyfills.d.ts
declare interface ObjectContructor {
  assign(target: any, ...sources: any[]): any;
}

but declaring it inside global, using module augmentation works

// polyfill.ts

declare global {
  export interface ObjectContructor {
    assign(target: any, ...sources: any[]): any;
  }
}

Object.assign = ...

Any idea why this is?

@mhegazy
Copy link
Contributor

mhegazy commented Aug 24, 2016

i would assume that the file polyfills.d.ts has a top level import or export, this turns it into a module. modules have their own scope, an this this ObjectConstructor is not really the ObjectConstructor but rather a local interface with the same name that shadows the global declaration.

using the declare global syntax, you are bypassing the scoping of the module and placing the declaration explicitly in the global scope, and this it merges with the intended ObjectConstructor declaration and you get the desired behavior.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

6 participants