Skip to content

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

Description

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);
        }
    }
    // ...

Activity

  1. yortus commented on Dec 21, 2015

    @yortus
    Contributor

    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
  2. thorn0 commented on Dec 21, 2015

    @thorn0
    Author

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

  3. RyanCavanaugh commented on Dec 21, 2015

    @RyanCavanaugh
    Member

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

  4. thorn0 commented on Dec 21, 2015

    @thorn0
    Author

    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 Troy Gerwien (@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.

  5. mhegazy commented on Jan 7, 2016

    @mhegazy
    Contributor

    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

  6. thorn0 commented on Jan 7, 2016

    @thorn0
    Author

    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.

  7. mhegazy commented on Jan 7, 2016

    @mhegazy
    Contributor

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

  8. thorn0 commented on Jan 7, 2016

    @thorn0
    Author

    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.

  9. added
    SuggestionAn idea for TypeScript
    DeclinedThe issue was declined as something which matches the TypeScript vision
    on Feb 20, 2016
  10. locked and limited conversation to collaborators on Jun 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScriptToo ComplexAn issue which adding support for may be too complex for the value it adds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions