using a interface to type a anonymous object in typescript
Asked Answered
S

4

26

I have a Interface IBase and a variable that contains a few other objects (in the sample i just added base for a better demonstration)

interface IBase {
    height?:number;
    width?:number;
}

var element = {
    base: {

    }
}

How can I say that the object that the varable element.base has is from the type IBase? I know that I could create a type for the element variable that contains the types of base etc, but is that also a possibility to type that scenario without doing so.

Soften answered 17/6, 2014 at 13:7 Comment(0)
R
32

Van den Brink's answer is good. Just as a demo another option :

var element = {
    base: <IBase> {

    }
}

This will give the desired intellisense as well :

enter image description here

Ruiz answered 17/6, 2014 at 13:46 Comment(1)
Using a type assertion here only gets you partial benefits because it will allow you to coerce to a type that your object literal doesn't actually meet the spec for. That is, it gets you intellisense but doesn't get you a good static type check. AFAICT the only way to get a static type check is to assign the literal to a variable of the correct type without a type assertion. Dick van den Brink has the better answer IMO.Delia
P
12

If you change the declaration of var element to the following it knows what base is:

var element: { base: IBase; } = {
    base: {
    }
}

Note: you can also create a new interface for it like this which makes it a bit more reusable: interface IElement { base: IBase; } and then use it like here: var element: IElement = { base: {} }

Protocol answered 17/6, 2014 at 13:11 Comment(0)
J
9

The excellent answer from @basarat now needs a little updating; using the angle brackets now produces a tslint error:

[tslint] Type assertion using the '<>' syntax is forbidden. Use the 'as' syntax instead. (no-angle-bracket-type-assertion)

The solution is a simple one:

const element = {
    base: {} as IBase
}

This also provides the intellisense (auto-complete) that you'd want as well.

Jounce answered 21/5, 2018 at 10:24 Comment(2)
This helped me greatly, as I was looking for an answer when applying a default fallback object when null-coalescing (??) through potential object sources. E.g.: const someData : SomeDataInterface = source1 ?? source2 ?? { /* Defaults */ } : SomeDataInterfaceLatitudinarian
Tslint is the one saying the syntax is forbidden. Angle brackets is still very much a valid way of castingValse
I
7

You have accidentally caused yourself a problem with your interface declaration. It is a subtle one that you will come across with structurally typed languages.

interface IBase {
    height?:number;
    width?:number;
}

Because all of the properties are optional, the interface is essentially {}. i.e. any object satisfies this interface. That's too general to be useful in most cases.

For example, it is perfectly acceptable to do this:

var x: IBase = {
    a: 'efsdsdf',
    v: 'sdfdsf' 
};

I think you'll agree that really this object is not useful anywhere I wanted to use an IBase object.

With this out of the way, you may find the most graceful solution is to create an interface that matches your element definition.

interface IElement {
    base: {
        height?:number;
        width?:number;
    }
}

var element: IElement = {
    base: {
        height: 4
    }
}
Intolerant answered 17/6, 2014 at 15:56 Comment(1)
Typescript may have improved this sine this post. Your example does give me an error: Type '{ a: string; v: string; }' is not assignable to type 'IBase'. Object literal may only specify known properties, and 'a' does not exist in type 'IBase'.(2322)Wellstacked

© 2022 - 2024 — McMap. All rights reserved.