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

Ability to access the type of a class outside the lexical scope it's defined in #6179

Closed
thorn0 opened this issue Dec 20, 2015 · 8 comments
Closed
Labels
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@thorn0
Copy link

thorn0 commented Dec 20, 2015

It's common and idiomatic in Angular 1.x apps to have code like this:

angular.module('app').factory('MyService', function(service1, service2) {
    class MyService {
        constructor() {
            this.foo = 2;
        }
        bar() {
            return service1(this.foo, service2);
        }
    }
    MyService.id = Math.random();
    return MyService;
});

Defining classes this way (using a factory function with dependencies injected to it as its parameters) is often the right thing to do from the point of view of Angular's approach to dependency injection because:

  1. In its methods, the class can use dependencies provided by the container (service1, service2).
  2. The class can have static properties (MyService.id) that, ideally, shouldn't be shared between different instances of the container.

However, this code can't be ported easily to TypeScript as the type MyService isn't visible for the rest of the app. We have to define also an interface for it and remember to change the interface every time the class is changed. I wish there were a way to make such types visible globally. Can't think of a good keyword for this. Probably, public or global will do.

angular.module('app').factory('MyService', function(service1: Service1, service2: Service2) {
    public class MyService {
        foo: number;
        constructor() {
            this.foo = 2;
        }
        bar() {
            return service1(this.foo, service2);
        }
    }
    // ...
@yortus
Copy link
Contributor

yortus commented Dec 21, 2015

Could #4233 help here? class MyService is effectively anonymous, but if typeof worked on expressions you could easily assign a type name to the return type of a function, something like:

function foo() {
    return class Anonymous {
        /*...*/
    }
}
type FooType = typeof foo(); // expression is not evaluated, just used to compute a type

@thorn0
Copy link
Author

thorn0 commented Dec 21, 2015

Oh, nice. Didn't see this interesting proposal about typeof expression. Of course, it would work too, although it needs a bit more code.

@RyanCavanaugh
Copy link
Member

Should we close this in favor of #4233, or is there a need for a plausible proposal?

@thorn0
Copy link
Author

thorn0 commented Dec 21, 2015

A closer look at #4233 made me think that for classes, that syntax wouldn't be elegant at all. Still much better than nothing though. In @yortus 's example above, the type that is assigned to FooType is not the type of instances of the Anonymous class, it's the type of its constructor. Both types would be most probably used by the code where this class would be injected to. So basically we'd have to write type ... = typeof ... twice:

type FooConstructor = typeof foo();
type FooInstance = typeof new (foo());

And such code would be needed for every class we want to make global. Whereas the syntax I proposed does the same with one modifier keyword. It'll make instance types global, and the constructor types of 'globalized' classes should be accessible via typeof, which sounds like a syntax conflict with #4233, by the way.

Effectively this modifier should work as if it created an ambient class declaration in the global scope:

function foo() {
  public class Bar {
    // ...
  }
}

is supposed to mean the same thing as

function foo() {
  class Bar {
    // ...
  }
}
declare class Bar {
  // ...
}

It's simple and readable. So, I'm against closing this issue.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 7, 2016

accessing a name outside its scope is not a possible. there are scoping and shadowing rules in effect already, and i do not see how you can make something like this work for the general case.
we have talked in the past about a way to access the instance type of a constructor function. i think this is another useful addition and I believe will address this use case better than #4233

@mhegazy mhegazy added the Too Complex An issue which adding support for may be too complex for the value it adds label Jan 7, 2016
@thorn0
Copy link
Author

thorn0 commented Jan 7, 2016

In JS, you can create a global variable at any point just by omitting var/let/const (well, we can't be sure it'll be really global) or by assigning to a property of the global object. So it's natural to expect similar scoping possibilities from types.

@mhegazy
Copy link
Contributor

mhegazy commented Jan 7, 2016

I do not think this specifically was a good design decision for JS :)

@thorn0
Copy link
Author

thorn0 commented Jan 7, 2016

If it comes about 'really global' variables, of course, I agree with you. But also we can do this:

var a;
function f() {
    a = ...
}

I wish we could do something like that with types. It'd be logical and symmetrical.

@mhegazy mhegazy added Suggestion An idea for TypeScript Declined The issue was declined as something which matches the TypeScript vision labels Feb 20, 2016
@mhegazy mhegazy closed this as completed Feb 20, 2016
@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
Declined The issue was declined as something which matches the TypeScript vision Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

4 participants