Skip to content

Use-case: annotation inheritance (source: Angular 2) #1

Description

@yjbanov

I'm logging this to document an additional use-case for this DEP and to double-check that the use-case is covered.

Angular 2 defines an annotation for users to specify which directives are used by a component. To illustrate, here's a simplified version:

// Defined by Angular 2
class View {
  final List<Type> directives;

  const View(this.directives);
}

The user uses this annotation as follows:

// User code
@View(const [Foo, NgIf])
class MyComponent {}

Here the annotation means "I intend to use directives Foo and NgIf in MyComponent".

Problem

It turns out that the vast majority of components end up using NgIf, NgFor and other standard directives. Only very code size conscious users would want to hand pick every single directive for their component. We therefore would like to define two annotations, @BaseView and an extension of it @View. The latter would automatically include several popular directives so the user doesn't have to specify them. Here's how we would like to define them:

/// Use this if you want to specify every directive used by a component.
class BaseView {
  final List<Type> directives;

  const BaseView(this.directives);
}

/// Use this if you want to specify only non-standard directives. Standard
/// directives are included by default.
class View extends BaseView {
  static const STANDARD_DIRECTIVES = const [NgIf, NgFor, NgClass];

  const View(List<Type> directives)
    : super(const [STANDARD_DIRECTIVES, directives]);
}

The user would then use them as follows:

// User specifies _precisely_ what they want
@BaseView(const [Foo, NgIf])

// User doesn't care much about extra size in JS output but
// doesn't like boilerplate. It's ok include all common directives,
// even though they only use some of them
@View(const [Foo])

The catch

The catch is that the initializer list of View is invalid Dart, as it expects that View may be used in a non-const context. If I understand this DEP correctly, it addresses this problem by allowing the initializer list to be the following:

  const View(List<Type> directives)
    : super([STANDARD_DIRECTIVES, directives]);

It would "inject" const into the list literal expression depending on whether the invocation is in a const context (which it is for annotations) or not.

Alternative: const class

A const class indicates that a class may only be instantiated as part of a const expression. new is not allowed. Here's how it would be declared:

const class View {
  static const STANDARD_DIRECTIVES = const [NgIf, NgFor, NgClass];

  const View(List<Type> directives)
    : super(const [STANDARD_DIRECTIVES, directives]);
}

Because the const modifier guarantees that the constructor is always invoked in a const context, it is safe to assume that directives constructor parameter is also a const value thus making the list literal expression valid.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions