Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Interface, enforce the type of extra properties [duplicate]

Let's say I want to create an interface to describe this type of objects:

let myObj= {
    "count": 3,
    "key1": "foo",
    "key2": "bar",
    "key3": "baz"
};

Those object always have a property count of type number and the rest of the properties are strings

If I define my interface using index signatures like this:

interface MyObect {
    count: number;
    [key: string]: string;
}

I got the compiler error:

[ts] Property 'count' of type 'number' is not assignable to string index type 'string'.

So I have to define it like this:

interface MyObect {
    count: number;
    [key: string]: any;
}

But this definition is not as presise.

Is there a way to enforce the type of extra properties ?

like image 658
Joseph Garrone Avatar asked Jun 04 '17 20:06

Joseph Garrone


2 Answers

I've achieved something like this by using an intersection type:

type MyObject =
{
    count : number
} & {
    [key : string] : string
}

This works (I am using TypeScript 2.3.2) for consuming an object, as follows

// x : MyObject
const count : number = x.count;
const foo : string = x.foo

but as pointed out, this assignment still fails

const x : MyObject = {
    count: 10,
    foo: 'bar'
}

so this may be of use only in some cases.

like image 171
Oly Avatar answered Sep 28 '22 07:09

Oly


This question has appeared in lots of subtly different forms over the past two months. Usually as a combination of a dictionary and an array.

As an alternative, you can use an error supression to keep the simplest definition of the type, which the TypeScript compiler is actually perfectly capable of working with.

It uses an error supression comment, which is generally a bad thing. It also requires a small additional effort at initialisation. The rest of the usage is as you would expect.

Here is a spin through your specific example using the dictarry technique.

interface MyObject {
    [key: string]: string;
    // @ts-ignore: I'm creating a Dictarray!
    count: number;
}

let myObj = { count: 0 } as MyObject;

myObj.key1 = "a string";
myObj.count = 4;

// Error, 1 is not a string - as you'd expect
myobj.key2 = 1;

myObj = {
    "count": 3,
    "key1": "foo",
    "key2": "bar",
    "key3": "baz"
} as unknown as MyObject;

const str = myObj.key1;
const num = myObj.count;
like image 41
Fenton Avatar answered Sep 28 '22 06:09

Fenton