170

Consider following example.

enum DialogType {
    Options,
    Help
}

class Dialog { 
    test() : string {
        return "";
    }
}

class Greeter {

    openDialogs: { [key in DialogType]: Dialog | undefined } = {
        0: undefined,
        1: undefined
    };

    getDialog(t: DialogType) {
        return this.openDialogs[t];
    }
}

const greeter = new Greeter();
const d = greeter.getDialog(DialogType.Help);
if (d) document.write(d.test());

Also in playground

There are 3 issues/questions with it:

  1. Why I cannot omit properties in my initializer literal, even though I declare properties as '| undefined'
  2. Why I cannot use 'DialogType.Options' as type key, and have to use hardcoded number instead?
  3. Why do I have to use 'key in DialogType' instead of 'key: DialogType'? (Or can I? )
1

2 Answers 2

287
  1. |undefined does not make a property optional, just means it can be undefined, there is a proposal to make |undefined members optional but currently it's not implemented. You need to use ? after ] to make all properties optional

    { [key in DialogType]?: Dialog }
    
  2. You can use the dialog enum values as keys, but they need to be computed properties:

    let openDialogs: { [key in DialogType]?: Dialog } = {
        [DialogType.Options]: undefined,
    };
    
  3. { [key: number or string]: Dialog } is an index signature. Index signatures are restricted to only number or string as the key type (not even a union of the two will work). So if you use an index signature you can index by any number or string (we can't restrict to only DialogType keys). The concept you are using here is called mapped types. Mapped types basically generate a new type based on a union of keys (in this case the members of DialogType enum) and a set of mapping rules. The type we created above is basically equivalent to:

    let o: { [DialogType.Help]?: Dialog; [DialogType.Options]?: Dialog; }
    
Sign up to request clarification or add additional context in comments.

7 Comments

That is brilliant, thank you so much! I was convinced that 'T?' was equivalent to 'T | undefined', especially after seeing this: typescriptlang.org/docs/handbook/…. So is optional type equivalent to something or is it kind of a thing on it's own?
@ironic It goes one way but not the other a property of type T? will be considered T|undefined for null checking, but a property of type T|undefined willnot be considered optional
What id I wanted to specify a different member type for each key? something like : let o: { [DialogType.Help]: DialogHelp; [DialogType.Options]: DialogOptions; }
@neomib you can do it manually like in your example, if there is some mapping rule I would need to see it to offer a better solution
key in ended up being the key part I was missing. ;) Thanks!
|
1

Here is an example of how to use enum as index in typescript:

enum DialogType {
    Options,
    Help,
    About
}
type Dialog = {
    whatever: string;
};
type DialogMap = { [key in DialogType]?: Dialog };

let o: DialogMap = {
    [ DialogType.Options ]: { whatever: "1" },
    [ DialogType.Help ]: { whatever: "2" }
};            

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.