associative array and structural subtyping

Topics: General, Language Specification
Oct 23, 2012 at 7:35 PM

In Kendo UI a new Model is defined and used as follows (see http://docs.kendoui.com/api/framework/model):

var Person = kendo.data.Model.define( {
    id: "personId", // the identifier of the model
    fields: {
        "name": {
            type: "string"
        },
        "age": {
            type: "number"
        }
    }
});

var person = new Person( {
    name: "John Doe",
    age: 42
});

The fields object is an associative array. Each element must structurally match the KendoFieldOptions interface as per the definition file I created below:

interface KendoEventObject extends JQueryEventObject {
    field: string;
    value?: any;
    action?: string;
    index?: number;
    items?: any[];
}

interface KendoObservableObject {
    uid: string;
    bind(eventName: string, handler: (eventObject: KendoEventObject) => any): void;
    get(name: string): any;
    parent(): KendoObservableObject;
    set(name: string, value: any): void;
    toJSON(): any;
}

interface KendoObservableArray {
    length: number;
    bind(eventName: string, handler: (eventObject: KendoEventObject) => any): void;
    join(separator: string): string;
    parent(): KendoObservableObject;
    pop(): any;
    push(...items: any[]): number;
    slice(begin: number, end?: number): any[];
    splice(index: number, howMany: number, ...items: any[]): any[];
    shift(): any;
    toJSON(): any[];
    unshift(...items: any[]): number;
}

interface KendoModel extends KendoObservableObject {
    dirty: bool;
    editable(field: string): bool;
    isNew(): bool;
}

interface KendoFieldOptions {
    defaultValue?: any;
    editable?: bool;
    nullable?: bool;
    parse?: (value: any) => any;
    type?: string;
    validation?: any;
}

interface KendoFields {
    [index: string]: KendoFieldOptions;
}

interface KendoModelOptions {
    id?: string;
    fields: KendoFields;
}

interface KendoModelStatic {
    define(options: KendoModelOptions): new (data?: any) => KendoModel;
}

interface KendoDataStatic {
    ObservableObject: new (data: any) => KendoObservableObject;
    ObservableArray: new (data: any[]) => KendoObservableArray;
    Model: KendoModelStatic;
}

interface KendoStatic {
    format(format: string, ...args: any[]): string;
    data: KendoDataStatic;
    observable(data: any): KendoObservableObject;
    observable(data: any[]): KendoObservableArray;
}

declare var kendo: KendoStatic;

As it happens all the properties of the KendoFieldOptions are optional, but even if I make one required, for instance the defaultValue property, I don't get any compiler warnings to say the defaultValue property is required when it is not specified. Is there a way TypeScript can help me to make sure each element in an associate array structurally matches an interface?

As a side note: I really wish Telerik would create a definition file themselves. If everybody creates one themselves everybody is going to come up with slightly different interface names etc. I'm sure that when Telerik do provide a definition file it will be different from mine in a breaking way. I'm thinking I should not adopt typescript until all libraries I use provide their own definition file.  It's a checken or egg issue as Telerik will probably not create one until TypeScript is more widely adopted. See http://www.kendoui.com/forums/integration/integration/kendo-ui-typescript.aspx

Coordinator
Oct 24, 2012 at 3:43 PM

I don't seem to see the problem you're mentioning.  When I make the change of making defaultValue required.  In the example below I get an error telling me that I did not create the object with the required defaultValue property: 

interface KendoFieldOptions {
    defaultValue: any;
    editable?: bool;
    nullable?: bool;
    parse?: (value: any) => any;
    type?: string;
    validation?: any;
}

var kfo : KendoFieldOptions = {};

It'd be great if JS library makers would also support .d.ts files. Even if they aren't using TypeScript directly, having the .d.ts files is a good way of having documentation about the library.

Oct 24, 2012 at 3:51 PM
Edited Oct 24, 2012 at 3:52 PM

When you do

var kfo : KendoFieldOptions = {};

then yes you get a compiler warning, but you do not get a compiler warning when you do:

var kmo: KendoModelOptions = {
    fields: { 
        "name": {} 
    }
};