How do I tell TypeScript about dynamic addition of properties?
Asked Answered
A

3

8

I'm using Backbone.Marionette in Typescript. I've written my own type description of Marionette.

var ProviderSpa = new Backbone.Marionette.Application();
ProviderSpa.addRegions({
    'servicesRegion': "#services-offered"
});
ProviderSpa.servicesRegion.show();

My problem is that addRegions has a side effect of adding properties to ProviderSpa, which TypeScript doesn't know about and therefore it complains that 'The property 'servicesRegion' does not exist on value of type 'Backbone.Marionette.Application'.

How can I tell TypeScript about these dynamic property additions to an instance of a type?

Adur answered 25/10, 2012 at 19:6 Comment(0)
A
3

I have used a cut down definition of BackBone Marionette for this example.

If you have lots of dynamic properties being added to an object, you might want to leave things dynamic rather than attempting to create declarations or interfaces for them all - especially as the overhead of keeping declarations up-to-date with new properties vs the benefits of having static typing on what are actually dynamic properties isn't a cost I would pay.

In this example, accessing the property using square-bracket syntax means it passes the type checking without the need for a declaration. The important bit is on the last line.

You can test this on the TypeScript playground.

declare module Backbone {
    export module Marionette {
        export class Application {
            addRegions(regions: any): void;
        }
    }
}

var servicesRegion = 'servicesRegion';
var ProviderSpa = new Backbone.Marionette.Application();
ProviderSpa.addRegions({
    servicesRegion: "#services-offered"
});
ProviderSpa[servicesRegion].show();
Aspirant answered 26/10, 2012 at 8:24 Comment(0)
P
2

Actually, TypeScript and Marionette fit hand in glove.
The key here is to think in terms of classes instead of dynamic properties. With that in mind, your code above becomes this:

class ProviderSpa extends Marionette.Application {
 servicesRegion: Marionette.Region;
 constructor(options?: any) {
  super(options);
  this.addRegions( { servicesRegion: "#services-offered" });
}
}

var providerSpa = new ProviderSpa();
providerSpa.show();

Update:
I've just posted part 1 of a blog article on TypeScript: Using Backbone.Marionette and RESTful WebAPI.

Peekaboo answered 28/2, 2014 at 5:24 Comment(0)
P
0

Runtime effects cannot be considered by the TypeScript type system as TypeScript types exist at compile time only. You will have to type ProviderSpa appropriately at compile time. Perhaps the easiest way to do it is make an interface for ProviderSpa that has addRegions and servicesRegion types so you can do:

interface IProviderSpa { servicesRegion: ...; addRegions: ...; };
var ProviderSpa = <IProviderSpa> new Backbone.Marionette.Application();

If Marionette has an interface type for application, you could even extend that like the following:

interface IProviderSpa extends Backbone.Marionette.IApplication { ... }
Polly answered 25/10, 2012 at 19:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.