annepic
annepic

Reputation: 1407

typescript check dynamically if string is a property of complex object

I really need to be able to check dynamically if aa is a property of my complex object called validations

const check = 'propertya' + 'a'; // result of a complex calculation that returns string 'propertyaa'

validations= {
    'a' : {
      'propertyaa': 'ax',
      'ab': 'ay',
      'ac': 'az'
    },
    'b' : {
      'ba': 'ax',
      'bb': 'ay',
      'bc': 'az'
    }
  };

if (this.validations.a[check] === undefined) {  ...

Error is:

element implicitly has an any type because type '{ ' propertyaa': string, 'ab': string, 'ac': string; }' has no index signature
(property) 'a': {
    'propertyaa': string;
    'ab': string;
    'ac': string;
}

Curiously the static (not dynamic) variant works if (this.validations.a['ab']) {

Upvotes: 2

Views: 1747

Answers (3)

annepic
annepic

Reputation: 1407

There is a simpler solution and really the only one that works for me so far:

cartoons = {
      'producers': {
          'Disney': 'Walt Disney',
          'PIXAR1': 'John John'
       }
    }

const check = 'Dis' + 'ney';
    const y = (<any>this.cartoons.producers)[check]; // notice the cast to any
    alert(y);

Upvotes: 0

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250156

You can go one of two routes. You can either add an index signature to your validation properties:

type Indexable = { [name: string]: string }
const validations = {
    'a': {
        'propertyaa': 'ax',
        'ab': 'ay',
        'ac': 'az'
    } as Indexable,
    'b': {
        'ba': 'ax',
        'bb': 'ay',
        'bc': 'az'
    } as Indexable
};

const check = 'propertya' + 'a';
if (validations.a[check] === undefined) {
}

You could also just cast the property to Indexable when you use it:

if ((validations.a as Indexable)[check] === undefined) {
}

You could also use any instead of Indexable, but the Indexable defined above provides more type safety then any

Another option is to assert that the string check is in fact a key for the value of a:

if (validations.a[check as 'propertyaa'] === undefined) {
}

Edit

If you go with the first option, a helper function can help avoid the need for the type assertion on each property of validations:

type Indexable = { [name: string]: string }
const validations = createValidations({
    'a': {
        'propertyaa': 'ax',
        'ab': '19',
        'ac': 'az'
    },
    'b': {
        'ba': 'ax',
        'bb': 'ay',
        'bc': 'az'
    }
});
function createValidations<T>(o: { [P in keyof T]: T[P] & Indexable}) : typeof o {
    return o;
}
const check = 'propertya' + 'a';
if (validations.a[check] === undefined) {
}

Upvotes: 1

Matěj Pokorn&#253;
Matěj Pokorn&#253;

Reputation: 17895

You will need index signature...

interface ComplexObject {
    [index: string ]: string | ComplexObject;
}

const check = 'propertya' + 'a';

const validations: ComplexObject = {
    'a' : {
      'propertyaa': 'ax',
      'ab': 'ay',
      'ac': 'az'
    },
    'b' : {
      'ba': 'ax',
      'bb': 'ay',
      'bc': 'az'
    }
  };

if (this.validations.a[check] === undefined) {
    // ...
}

Upvotes: 0

Related Questions