0

I have a series of UI widget components in a one module in an Angular 5 application. An separate application module uses those widgets and needs to provide a configuration object for each one. While creating the configuration object in my editor (VS Code), I'd like the editor to validate the configuration object as well as provide intellisense.

Ideally, I'd like to define an interface for each widget in its component.ts file - it's easy to update both the component and the interface at the same time that way. Alternatively, I could have a single file of interfaces.

I understand that interfaces aren't used at compile time. I just want them to be available when I'm setting up a configuration object in my application module. What is the best way to do this?

Option A: Namespaces
Based on this article I've been looking at namespaces. I like the idea of adding to a single Config namespace from multiple files. However, I've seen several opinions like this answer on SO that say namespaces are bad.

Because I believe that namespaces are a TS thing, not an Angular thing, I've tried this:

config.namespace.ts (Currently a single file - once that works I'll try to distribute it to multiple files.)

export namespace Config {
    export interface Breadcrumbs {
      responsive: boolean;
      links: BreadcrumbLink[];
    }
    interface BreadcrumbLink {
      label: string;
      link?: string;
    }
}

app-config.service.ts

/// <reference path="/path/to/config.namespace.ts" />

@Injectable()
export class AppConfigService {
  config: any = {};
  constructor() { }

  configureWidgets() {
    const breadcrumbWidget: Config.Breadcrumbs = {
      'responsive': true,
      'links': [
        { 'label': 'Home', 'link': 'http://...' },
        { 'label': 'Main Section', 'link': 'http://...' },
        { 'label': 'Current Page' },
      ]
    };
    this.config.breadcrumbs = breadcrumbWidget;

Unfortunately, it's not pulling the Config namespace to use in the editor.

Option B: .d.ts file
I'm new to this concept, but it looks like it's defining types that the package.json then pulls in and makes generally available. This might work, but it means that I couldn't keep the interfaces in each widget's file, right where they're the most useful. Also, most of the articles about .d.ts files assume that you're setting up typings for a package, which isn't the case here.

Option C: Import each interface directly

import { Breadcrumbs } from './path/to/config.namespace.ts'

This works, but it doesn't feel right. This will lead to a long set of interface imports and specific paths if I keep each interface with it's widget.component.ts file. Also, I believe that imports are mainly for pulling in other functionality that you're going to use in the application, not just for convenience in the IDE?

Scott McD.
  • 129
  • 1
  • 13

1 Answers1

0

I ended up going with a version of Option C, but using a barrel file to simplify the importing. Each widget's component.ts file includes an interface to use when configuring it. The barrel file then re-exports all of them:

export { Breadcrumbs } from './path/to/component';
export { Button } from './path/to/component';
export { DialogButton } from './path/to/component';

Then, in the service that sets up the configuration for each widget, I import everything from the barrel file with:

import * as Config from '../../path/to/barrel/widget-interfaces.index';

Finally, when I create an configuration object for a new widget, I declare the interface needed:

    const app_breadcrumbs: Config.Breadcrumbs = {
      responsive: true,
      links: [
        { label: 'Home', link: '//www.homeurl.com/index.html' },
        { label: 'Main Section', link: '//www.homeurl.com/main-section/main.html' },
        { label: 'Current Page' }
      ]
    };

I'm using VS code, and it suggests keys as I start typing and validates that the types and object shapes are correct.

Scott McD.
  • 129
  • 1
  • 13