Home > Blockchain >  Check for null values in an object
Check for null values in an object

Time:08-17

I am new to typescript and I was wondering if there is a more efficient way to check for null values in an object besides using a for loop? Example below:

interface testType {
    one: string | null;
    two: string | null;
    three: string | null;
    four: string | null;
}

const dict: testType = {
    one: "hello",
    two: "world",
    three: null,
    four: "!",
};

let field: keyof testType;
for (field in dict) {
    if (dict[field] == null) {
        console.log('null');
    } else {
        console.log('exist');
    }
}

CodePudding user response:

Object.values() and some() will combine nicely to do it...

Object.values(testType).some(value => value === null)

Docs: Object.values() and some

CodePudding user response:

every works too. You can increase solutions with standard built-in methods.

const isNull = Object.values(object).every(x => (x === null || x === ''));

CodePudding user response:

Iteration is required in this scenario, so some kind of "loop" is the only way to check each value.

The only type-safe way to iterate the expected keys (or values at the expected keys) in an object in TypeScript is to maintain a list of the keys and use them for iteration. In the example below, I use an array of keys (initialized with a const assertion) along with the type utility Record<Keys, Type> to build the type of the interface in your question. Then the array of keys is used to iterate the object annotated by the same type:

TS Playground

const keys = ['one', 'two', 'three', 'four'] as const;

type DictKey = typeof keys[number];
   //^? type DictKey = "one" | "two" | "three" | "four"

type Dict = Record<DictKey, string | null>;
/*   ^ This type looks like:
type Dict = {
  one: string | null;
  two: string | null;
  three: string | null;
  four: string | null;
}
*/

let dict: Dict = {
  one: "hello",
  two: "world",
  three: null,
  four: "!",
};

for (const key of keys) {
  const value = dict[key];
  if (typeof value === 'string') {
    console.log(`${key} exists:`, value);
                                //^? const value: string
  } else {
    console.log(`${key} is ${value}`);
                           //^? const value: null
  }
}

The reason for this is that TypeScript is structurally-typed, so it allows for extra (excess) properties in object values assigned to an object type — as long as all the expected properties are there and their values are of the correct type.

const anotherValueThatIsAssignableToDict = {
  one: 'goodnight',
  two: 'moon',
  three: null,
  four: null,
  booleanProp: false,
  numberProp: 22,
  stringProp: 'unexpected',
};

dict = anotherValueThatIsAssignableToDict; // TypeScript is ok with this

Trying to iterate the keys of an object type using key in object or Object.keys will always result in string key types:

for (const key in dict) {
  const value = dict[key]; /*
                ~~~~~~~~~
  Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Dict'.
  No index signature with a parameter of type 'string' was found on type 'Dict'.(7053) */
}

And, if you simply iterate the values in the object (like in this answer), you'll get all of the excess values, too, which is very unlikely what you want:

for (const value of Object.values(dict)) {
  console.log(value); // Iteratively logs each value in "anotherValueThatIsAssignableToDict"
}

So, maintaining a list of the keys yourself is the only type-safe way, and it is also more performant because it skips iterating the excess entries in the object.

  • Related