46

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,513
  • 1
  • 15
  • 33
  • Related: [Enforcing the type of the indexed members of a Typescript object?](https://stackoverflow.com/q/13315131) – VLAZ Apr 26 '22 at 10:36

3 Answers3

57

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"];
Paolo
  • 17,649
  • 21
  • 81
  • 115
Seamus
  • 4,023
  • 2
  • 32
  • 41
  • 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
4

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
  • 303
  • 2
  • 10
sgrtho
  • 218
  • 1
  • 6
-5

Now use can do things with ES6 new feature Map:

let map: Map<string, EventEmitter<any>> = new Map<string, EventEmitter<any>>();
Downhillski
  • 2,381
  • 2
  • 25
  • 37