5

I have a const object which looks like this:

export const Language: Values = {
  es: {urlValue: 'es', label: 'Spain'},
  en: {urlValue: 'en', label: 'Anything'},
  eb: {urlValue: 'eb', label: 'Other thing'},
};

export interface Values {
  [name: string]: Value;
}

export interface Value {
  urlValue;
  label;
  selected?;
}

Now somewhere in app, we read some data which has this format, which coincidentally has the exact same keys:

{
  es: 32,
  en: 11,
  eb: 56
}

So actually i need another object type like this:

export class AnotherObject {

  constructor(public es: number,
              public en: number,
              public eb: number) {
  }
}

But can i create this type also dynamically so that it automatically uses the keys of the Language object, or urlValue of "Value" type?

PS: The question is a simplified form of our exact use case, where we have multiple consts like Languages. So an automatism would be really helpful.

Edit: For the suggestion of @basarat - Since this object is a part of a parent object i actually need a type definiton in this part:

export class ParentObject {
  thatObjectAbove: AnotherObject;
  otherObjects: ..
  ..
}

export interface AnotherObject {
  [name: LanguageOptions]: number;
}

I have the error "An index signature parameter type cannot be a union type. Consider using a mapped object type instead." on the 'name' part of line "[name: LanguageOptions]: number;"

3 Answers 3

3

The keyof type operator gives you the key names as a type:

export const Language = {
  es: {urlValue: 'es', label: 'Spain'},
  en: {urlValue: 'en', label: 'Anything'},
  eb: {urlValue: 'eb', label: 'Other thing'},
};

type LanguageOptions = keyof typeof Language; // 'es' | 'en' | 'eb'
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. But I have this error on the last line where you use keyof: 'Language' refers to a value, but is being used as type here.
Fixed. You need to get the type of the variable with typeof and then use keyof to get the keys of the type.
ok.. but still i can't use this 'type' as a key type... i edited the question.
1

Continuing this answer:

I have achieved my goal with defining the AnotherObject as type rather than interface.

type LanguageOptions = keyof typeof Language;  // as from basarat's answer

type AnotherObject = {[key in LanguageOptions]: number}; // is actually a type, still is named as 'object' so that it is still compatible with the question's code
    
export class ParentObject {
  thatObjectAbove: AnotherObject;
  otherObjects: ..
  ..
}

As it is stated here, it is because of the strange behaviour of typescript, which can be fixed in this way.

So the end solution in one-liner:

thatObjectAbove: {[key in keyof typeof Language]: number};

or

thatObjectAbove: Record<keyof typeof Language, number>;  // Record is a built-in typescript type

Comments

0

Would you might to change your code a bit?

class DynamicClass<T> {
    constructor(values?: { [key in keyof T]?: T[key] }) {
        for (const key of Object.keys(values)) {
            this[key] = values[key];
        }
    }
}

class MyClass extends DynamicClass<MyClass> {
    attr1: string;
    attB: number;
    obj: {
        wow: string,
        yeah: boolean
    };
}


new MyClass({
    attr1: 'a',
    obj: {
        wow: '',
        yeah: true
    },
    att
})

enter image description here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.