Typescript: Decorating a declared class property should be an error

Created on 29 Feb 2020  ·  5Comments  ·  Source: microsoft/TypeScript


TypeScript Version: ^3.7


Search Terms:

declare decorator decorated class property

Code

function baz(_target: Object, _key: string) {};

class Foo {
  @baz declare bar: string;
}

Expected behavior:

Error due to decorating ambient context.

Actual behavior:

No error, decorated property is emitted (exact same output as without declare).

Playground Link:

https://www.typescriptlang.org/play/?experimentalDecorators=true&ssl=6&ssc=2&pln=2&pc=1#code/FAMwrgdgxgLglgewgAgEYEMBeAKA+jdAJwHMBTGALmQHlUArU2AGmVwGtSBPKgZxkLgRiASmQBvAL4BuYMCgAbdDx7IAYggTjgyZAAEMmZABNGiwqTRFe-QcRkSgA

Related Issues:

Working as Intended

Most helpful comment

What about using definite?

  @Inject('AnyNamespace.AnyInterface')
  private Foo!: AnyNamespace.AnyInterface;

All 5 comments

Is this an error or a feature? I think it's useful since I'm using this pattern for dependency injection.

````
class A {
@Inject('AnyNamespace.AnyInterface')
declare private Foo: AnyNamespace.AnyInterface;

method() {
this.Foo.blabla... // <-- this.Foo is type-safe.
}
}
``` DI module will inject to the propertyFoofrom decorator, so the assignment could be omitted. However, since I enabledstrictPropertyInitialization: true, property declaration without initialization will result in an error. So thedeclare` keyword is needed.

If it turns to be an error, the injection to property have to be written as
@Inject('AnyNamespace.AnyInterface') private Foo: AnyNamespace.AnyInterface = undefined as any;

What about using definite?

  @Inject('AnyNamespace.AnyInterface')
  private Foo!: AnyNamespace.AnyInterface;

What about using definite?

  @Inject('AnyNamespace.AnyInterface')
  private Foo!: AnyNamespace.AnyInterface;

Sorry for the late reply, but declare foo: any and foo!: any may have different behavior: The latter will overwrite its value when useDefineForClassFields: true is set.

declare function SomeDecorator(...args: any): any;

class A {
    foo: string = 'foo';
}

class B extends A {
    @SomeDecorator
    declare foo: string;
}

class C extends A {
    @SomeDecorator
    foo!: string; // <- warning here.
    // Property 'foo' will overwrite the base property in 'A'. If this is intentional, add an initializer. 
    // Otherwise, add a 'declare' modifier or remove the redundant declaration.(2612)
}

playground link

Sometimes I don't need to overwrite the value, only want to add some behavior by using a decorator.
There will be no way to do it if decorating a declared property becomes an error.

This is actually an intended behavior, and one of the reasons declare exists. If you are compiling targeting native fields, or with --useDefineForClassFields, redeclaring a field in a subclass just to decorate it would _actually overwrite the field_, as @whzx5byb mentions above. We allow you to decorate a declare field as that is the only way to decorate the field on a subclass without overwriting it.

Thanks, @rbuckton! TBH I should have closed this months ago. Decorated declare properties is a pattern I now use regularly. I even wrote a blog post about it! 😆

Was this page helpful?
0 / 5 - 0 ratings