Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'Class' is referenced directly or indirectly in its own type annotation

Here is basic DI pattern:

class Foo {
  foo = 1;
}

class Bar {
  constructor(public Foo: typeof Foo) {
    const foo = new Foo();
  }
}

class Baz extends Foo {}
new Bar(Baz);

It gives an error:

'Foo' is referenced directly or indirectly in its own type annotation.

But it clearly isn't self-referential, because public Foo is property name, and typeof Foo is type.

What is going on here? Is it TypeScript bug that is expected to be solved in future? Is it documented?

Can Foo property name be kept somehow here without renaming it? It just makes sense to name it so.


It appears that public Foo: Foo doesn't create problems with types, and Foo refers to original Foo interface in public AnotherFoo: Foo:

class Bar {
  constructor(public Foo: Foo, public AnotherFoo: Foo) {
    const foo: Foo = AnotherFoo;
    foo.foo;
  }
}

And typeof Foo refers to Foo parameter type and not original Foo interface:

class Bar {
  constructor(public Foo: number, Quux: typeof Foo) {
    const quux: number = Quux; 
  }
}
like image 500
Estus Flask Avatar asked Feb 06 '18 10:02

Estus Flask


People also ask

Is there a type annotation for 'locationtype'?

}), }); 'LocationType' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. You just have to specify the LocationType type to GraphQLObjectType.

Does a key reference itself in its initializer?

So key does indeed indirectly reference itself in its initializer… and this is pretty solidly "design limitation" territory. Of course, at many of these above bullet points, a reasonable human being may well differ in behavior from the compiler. For example, consider The type of current.x + ',' depends on the type of current.x and the type of ','

How to search for a symbol with an annotation?

If the annotation is a name when searching for the symbol, only types are considered as only they can be valid. After typeof the name of a symbol that has a type is expected (class, local variable, parameter, etc.). So then the search must include parameters and the Foo parameter is in scope so it must be considered.

Is 'Foo' a self-referential class?

class Foo { foo = 1; } class Bar { constructor (public Foo: typeof Foo) { const foo = new Foo (); } } class Baz extends Foo {} new Bar (Baz); 'Foo' is referenced directly or indirectly in its own type annotation. But it clearly isn't self-referential, because public Foo is property name, and typeof Foo is type.


1 Answers

The problem is that inside the constructor Foo will refer to the parameter not the class, so you can't reference Foo by name alone. The simplest way to do it and keep the name of the parameter is to declare a type alias:

class Foo {
    foo = 1;
}
type FooType = typeof Foo
class Bar {
    constructor(public Foo: FooType) {
    }
}

The reason Foo : Foo works but Foo: typeof Foo does not work is that after the type : a type annotation is expected. If the annotation is a name when searching for the symbol, only types are considered as only they can be valid.

After typeof the name of a symbol that has a type is expected (class, local variable, parameter, etc.). So then the search must include parameters and the Foo parameter is in scope so it must be considered.

Edit

@artem makes a good point that I did not explicitly call out above:

[This behavior] is not specific to the constructors. In typeof Foo, Foo refer to the nearest value (not type) named Foo in the scope. Once you have a parameter named Foo, it's in the scope (you can even use it as default value to initialize other parameters), and it shadows any outer values named.

like image 74
Titian Cernicova-Dragomir Avatar answered Oct 10 '22 17:10

Titian Cernicova-Dragomir