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

Make variable declarations open ended #819

Closed
clark-stevenson opened this issue Oct 3, 2014 · 43 comments
Closed

Make variable declarations open ended #819

clark-stevenson opened this issue Oct 3, 2014 · 43 comments
Labels
Needs More Info The issue still hasn't been fully clarified Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript

Comments

@clark-stevenson
Copy link

This used to be on codeplex and I cannot find a reference here.

https://typescript.codeplex.com/workitem/917

The problem is that I have project.d.ts and lets say that it has a Point class with simple {x,y} properties.

project-plugin-coolvector.d.ts might come along tomorrow, and that Plugin JS might add a z property to the existing Point class.

If project-plugin-coolvector.d.ts attempts to export a Point class with a z property. You get a duplicate error. I looked at the JQuery plugins example on DT but project.d.ts must contain classes, since Point has a constructor.

I see no way of being able to support the direction of the project in this instance.

@danquirk
Copy link
Member

danquirk commented Oct 6, 2014

It's not clear there's a good way to do this for var that doesn't lead to a lot of unsafe code. It's possible there's a way to do this for classes. It would be helpful to understand why you need to have open ended classes and where open ended interfaces are insufficient.

@danquirk danquirk added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Needs More Info The issue still hasn't been fully clarified labels Oct 6, 2014
@clark-stevenson
Copy link
Author

Thanks for your response 👍

In this particular project, there is a Game class in JS https://github.com/photonstorm/phaser/blob/master/src/core/Game.js

With its accompanying definition
https://github.com/photonstorm/phaser/blob/master/build/phaser.d.ts#L1948

And then there is a totally external plugin
https://github.com/lewster32/phaser-plugin-isometric/blob/master/src/Isometric.js#L50

It adds "iso" to the existing Game Class. Because the first link Game.js is a Class, it seems logical that the definition is a class. I cannot see how to make it an interface.

@clark-stevenson
Copy link
Author

I seen this again today in a different project.

There was a Core class, and an extension added a new method called to the Core prototype time. Since Time.js is a class, and Core.js is a class. The property time cannot be added to core when the definition for time.d.ts is added.

I frequently come up against this problem and cannot seem to resolve it. I run into the interface suggestion but if that is the case, should classes simply be avoided?

@lucamorelli
Copy link

well, especially for definition files may be a useful features, because I see several libraries that when included add members to existing type. I don't know if is good to add this in your own code

@saschanaz
Copy link
Contributor

I think classes will be required to be open-ended when browsers support subclassing from Array. https://status.modern.ie/subclassinges6
https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array-constructor

For open-ended variables, are there some reasons not to do this?

declare var x: SomeInterface;
extend x: ExtensionInterface;
//x now becomes SomeInterface + ExtensionInterface

/*
same as
interface X { ... }
interface X { ... }
*/

@joepb
Copy link

joepb commented Nov 6, 2014

In JavaScript we can extend ECMA classes:
Date.foo = function() {...}
One would expect this to be somehow supported by the TypeScript superset.

@basarat
Copy link
Contributor

basarat commented Jun 4, 2015

Proposal

If the following is declared:

declare class Foo  {
    static bar: any;
    bas():any;
}

It can be extended using another declaration:

// Amend Foo
declare class Foo  {
    static anotherStatic: any;
    anotherMember():any;
}

Reason

@danquirk

It would be helpful to understand why you need to have open ended classes and where open ended interfaces are insufficient.

Really for a convinient way to model classes. Currently one needs to rewrite the original definition to make it extensible. This is not simple to explain to someone brand new to TypeScript.

declare var Foo : FooStatic;
interface FooStatic { // Static properties
    new (): Foo;
    bar: any;
}
interface Foo{ // Instance properties
    bas():any;
}

// Amend Foo 
interface FooStatic  {
   anotherStatic: any;
}
interface Foo{
    anotherMember():any;
}

@NathanWalker
Copy link

+1 this is very frustrating to not have at the moment. Please please, need this.

@Pro
Copy link

Pro commented Aug 5, 2015

👍

@adidahiya
Copy link
Contributor

#2957 looks like it will cover the use case for open-ended classes

@lucax88x
Copy link

need!!!

@DanielRosenwasser DanielRosenwasser changed the title Make class / variable declarations open ended Make variable declarations open ended Sep 16, 2015
@windperson
Copy link

+1

@JoeDuncko
Copy link

Been hitting my head up against this for a while. If we want the community to be able to share and depend on each others' d.ts files, then we will need to be able to extend them.

@niemyjski
Copy link

+1

1 similar comment
@ravishivt
Copy link

+1

@nin-jin
Copy link

nin-jin commented Dec 19, 2015

+100500

@DomiR
Copy link

DomiR commented Jan 8, 2016

+1

1 similar comment
@delphinus
Copy link

+1

@englercj
Copy link

englercj commented Mar 9, 2016

+1 This is extremely useful when you write a "plugin" for some other library that adds methods to their existing classes. I would expect my plugin's .d.ts file to be able to extend the existing classes with new methods.

@istvan89
Copy link

+1

@suppayami
Copy link

+1

@garthk
Copy link

garthk commented May 20, 2016

Required for hapi, where plugins such as inert can add members to IRouteConfiguration.handler, as DefinitelyTyped would have it. To work around it, I'd have to do something like:

    const routeConfig: IRouteConfiguration = {
        path: '/',
        method: 'GET',
        handler: {
            // will cheat below to assign file:
        }
    };

    assign(routeConfig.handler, {
        file: {
            path: indexFile
        }
    });

    server.route(routeConfig);

Too much of this will maim TypeScript's advantage over JavaScript for this project.

@jocull
Copy link

jocull commented May 29, 2016

Please consider adding this. Extending external JavaScript packages is very painful. Adding new methods to existing prototypes seems impossible. Extending them into new definitions is not practical, as you must then override them in every other case to become a new type. They quickly conflict and become incompatible and awkward to use. This is especially true of objects that "chain" and return themselves.

http://stackoverflow.com/questions/37505881/extending-typescript-declaration-files

@mhegazy
Copy link
Contributor

mhegazy commented May 31, 2016

@jocull this can be achieved using module augmentation today. see my response in http://stackoverflow.com/questions/37505881/extending-typescript-declaration-files?answertab=votes#tab-top

@JeroenNelen
Copy link

+1

@mertdeg
Copy link

mertdeg commented Oct 31, 2016

+1

@simonbengtsson
Copy link

Another library where this would be helpful is jspdf. It exports a class let doc = new jsPDF(). Third party plugins can be added and would be called like this doc.anExternalPlugin(). One example plugin is jspdf-autotable.

@John0x
Copy link

John0x commented Mar 8, 2017

+1
This sadly keeps me from using Typescript with VueJs

@mhegazy
Copy link
Contributor

mhegazy commented Mar 9, 2017

This sadly keeps me from using Typescript with VueJs

can you elaborate?

@John0x
Copy link

John0x commented Mar 9, 2017

@mhegazy With Vue 2 you can access Vue plugins by the Vue class instance. For example the cookie plugin would be Vue.cookie

Therefore I would have to extend the type declaration for the Vue class, which I can currently not do (as far as I can see) since we can't merge class declarations.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 9, 2017

import * as Vue from "vue";

declare module "vue" {
    export var plugin: any; // my new plugin
}

Vue.plugin; // OK
new Vue(); // OK

@John0x
Copy link

John0x commented Mar 10, 2017

@mhegazy thanks, I'll try that out, once I'm home :)
Does it work if you have two different declaration files too?

Original (index.d.ts and my own declaration.d.ts)?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 10, 2017

Vue declaration comes from the declaration file coming from the Vue npm package. i do not know what is the other one.

@John0x
Copy link

John0x commented Mar 10, 2017

@mhegazy yeah, that's the one I meant with index.d.ts)

@icewind
Copy link

icewind commented Mar 21, 2017

This is sad... Most of modules I'm using(typeorm as example) using class instead of interface in their declaration. I can extend them easily in JS but it is impossible in TS(keep type info + autocomplete).

@mhegazy
Copy link
Contributor

mhegazy commented Mar 21, 2017

classes are extensible through interfaces (instance side), and through namespaces (static side). not sure why is variable declarations merging or not is relevant here.

@maxlk
Copy link

maxlk commented Jun 22, 2017

@mhegazy Class (instance) may be extended through interface only if it's declared in global namespace. It doesn't work when class is defined in some namespace:

declare namespace ns {
    declare class Foo { }
    declare function getFoo(): Foo;
}

namespace ns {
    interface Foo {
        bar(): number;
    }
}

let foo: ns.Foo = ns.getFoo();
foo.bar(); // Error: Property 'bar' does not exist on type 'Foo'

(the same works when removing ns namespace)

@manast
Copy link

manast commented Nov 1, 2017

It would be quite interesting to hear from the maintainers of typescript the reason why this issue has not been addressed. Is it there a theoretical standpoint that prevents this for being implemented? Is it that you believe this issue is not important, or easy workarounds are available? Some other reason? Why is this issue still open after 3 years?

@jocull
Copy link

jocull commented Nov 1, 2017

I have worked around this in particular cases by extended a local interface definition. This is not ideal, but it works like this:

interface ResponseExt extends express.Response {
    myNewProperty: string;
}

Then you'd just declare usage of ResponseExt explicitly anywhere you use it, such as a function argument. Again, not my favorite thing, but it's the only workaround I could come up with.

@manast
Copy link

manast commented Nov 1, 2017

@jocull yes, but what about methods? I would like to extend a class with new methods as in aspect oriented programming...

@jocull
Copy link

jocull commented Nov 2, 2017

@manast Would you mind putting an example here for clarity?

@nolakara
Copy link

+1

@RyanCavanaugh
Copy link
Member

The OP is extremely old here and references a link that doesn't work anymore. In the interim we've added external module augmentation, additional forms of permitted declaration merging, and removed some error cases that made declaration merging less ergonomic.

With secure JS runtimes being more common (thus no arbitrary mutation to built-ins) and modules exporting immutable surface area in modern (thus you only can augment exposed endpoints, which are already eligible for declaration merging) I don't think there's any necessary work to do in this area. Open to new suggestions on more targeted areas but it's not clear that "make variable declarations open-ended" is a tractable description or the right path forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests