63

I came across this line in three.d.ts:

dispatchEvent(event: { type: string; [attachment: string]: any; }): void;

and was wondering what it meant.
I understand that this would mean a function called dispatchEvent which takes an argument of a type with a member type but I am not sure what:

[attachment: string]: any;

means.

trinalbadger587
  • 1,905
  • 1
  • 18
  • 36
  • Related: [Enforcing the type of the indexed members of a Typescript object?](https://stackoverflow.com/q/13315131) – VLAZ Apr 26 '22 at 10:36

4 Answers4

74

That is an index signature. From the TypeScript documentation:

Indexable types have an index signature that describes the types we can use to index into the object, along with the corresponding return types when indexing.

So, for example, you could define an interface for an indexable object like:

interface IArrayOfStrings {
    [index: number]: string;
}

This tells the compiler, that for any object of type IArrayOfStrings, any member accessed by the numerical index will be of type string.

So, this will compile without error:

interface IArrayOfStrings {
    [index: number]: string;
}

let words: IArrayOfStrings = ["foo","bar"];

let word: string = words[0];

But this will not:

interface IArrayOfStrings {
    [index: number]: string;
}

let words: IArrayOfStrings = ["foo","bar"];

let myNumber: number = words[0];

In your example, this line:

dispatchEvent(event: { type: string; [attachment: string]: any; }): void;

is describing a method dispatchEvent that accepts one parameter of type { type: string; [attachment: string]: any; }.

To make that type easier to understand, look at an interface that defines this type:

interface IEvent {
    type: string;
    [attachment: string]: any;
}

This tells the compiler that objects of type IEvent will have a string property called type, and elements of an IEvent object, accessed by the string index will be of any type.

So, something like this would compile without error:

interface IEvent {
    type: string;
    [attachment: string]: any;
}

let myEvent: IEvent = {
    type: 'some-event-type'
};

let eventType: string = myEvent["type"];
Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Seamus
  • 4,539
  • 2
  • 32
  • 42
  • 1
    so this means that the string 'attachment' used in the declaration is just a placeholder but it has nothing to do with the concrete values that follow this type, is this correct? (says 'attachment' but it could be 'oranges' and it will work too) – Fernando Gabrieli Jan 21 '21 at 20:28
  • 2
    @FernandoGabrieli yes, as far as I can tell, you are correct. You could use anything there. I always use "index" because I think that makes it clearer. But, yes, you can use anything and I don't know that it makes any real different. In fact if you look at the javascript generated by the compiler, it does not change when you change that property name. – Seamus Jan 23 '21 at 16:45
  • @Paolo - Maybe you could shed some light on this... How can you ask subproperties deeper than one level? Let's suppose that `type: {name: string}`. How can I access the `name` type? `IEvent["type"]["name"]` does not seem to work. – Nico Mar 12 '21 at 14:49
  • 1
    @Seamus I guess the above comment was for you. – Paolo Mar 12 '21 at 16:49
  • This answer did not help me! Can anyone infer the answer to this question: https://stackoverflow.com/questions/71787973/weird-typescript-syntax-what-can-be-a-sample-member-of-this-type?noredirect=1#comment127005957_71787973? – Samuel Nihoul Apr 17 '22 at 15:58
  • The documentation listed in the question is deprecated; the new version is [here](https://www.typescriptlang.org/docs/handbook/2/objects.html#index-signatures). – jcalz Feb 28 '23 at 21:45
5

The brackets declare an index signature, meaning beside type, which is mandatory, you can put anything into the first argument.

Basically this weakens the type safety of the argument. This mechanism is of great use if the function is not itself a consumer but a generic interlink between players using stronger typing (they'll have deeper knowledge of the event structure).

I added another answer because the existing answer named this an optional argument, which it is not. An optional argument is postfixed with a "?" and quite different.

Marek Kamiński
  • 409
  • 5
  • 11
sgrtho
  • 228
  • 1
  • 6
0

Index Signatures:

Your type means the following:

type Event = {type: string, [attachment: string]: any };

The square brackets means that we are talking about an object type. it has one property called 'type' of type string.

The other part and is called a index signature. What this does it that is maps properties which are not known at compile time to a type. Because the type of the value of the index signature is any this means these unknown properties can be of any type. For example the following would be valid:

const event: Event = {type: 'abc', bar: {'anything': 123, 'jow': 453}, booo: true};

Basically the only restriction is that we have a property named bar of type string.


Note that the name attachment is arbitrary and any other name could be used without changing the meaning. For example the following snippet is semantically equivalent to the previous one:

type Event = {type: string, [prop: string]: any };
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
-6

Now use can do things with ES6 new feature Map:

let map: Map<string, EventEmitter<any>> = new Map<string, EventEmitter<any>>();
Downhillski
  • 2,555
  • 2
  • 27
  • 39