How do I properly traverse an object in Ts

The article was first published on a personal blog

This is a short article…

JavaScript

Before going over an object with Ts, let’s talk about how to implement this in Js. In, object. keys, a simple example:

// for... in
const obj = {
  name: 'itsuki'.address: 'hangzhou'};for (const key in obj) {
  console.log(key, obj[key].toUpperCase());
}
// Object.keys
Object.keys(obj).forEach(key= > {
  console.log(key, obj[key].toUpperCase());
});

/ / output
// name ITSUKI
// address HANGZHOU
Copy the code

TypeScript

for… in

But in TypeScript, if you do this directly, you’ll get an error.

type Person = {
  name: string;
  address: string;
};
const obj: Person = {
  name: 'itsuki'.address: 'hangzhou'};function print(obj: Person) {
  for (const key in obj) {
    / / ❌
    // key:string cannot be assigned to {name:string; Age: number} type
    console.log(key, obj[key].toUpperCase());
  }
}
print(obj)
Copy the code

We know for… Keys can’t be assigned to a Person’s name or address because all keys in an Object are strings.

But we can solve this problem by keyof.

function print(obj:Person){
  let key: keyof Person;
  for (key in obj) {
    / / ✅
    console.log(key, obj[key].toUpperCase()); }}Copy the code

Object.keys

When using object. keys, we can use the AS operator.

function print(obj: Person) {
  Object.keys(obj).forEach((k) = > {
    / / ✅
    console.log(k, obj[k as keyof Person].toUpperCase());
  });
}

Copy the code

We can pull this out as a function:

function getKeys<T> (obj: T) {
  return Object.keys(obj) as Array<keyof T>;
}

getKeys(obj); // (keyof Person)[]
Copy the code

Object.entries

We can also use object.entries () to traverse objects.

Object.entries(obj).forEach(([k, v]) = > {
  console.log(k, v);
});
Copy the code

thinking

The following is my own thinking, if there is any mistake, please correct

I think the Object. The keys () returns a string [], because it is set at run time, that we do know the TypeScript is static type checking, even if we use keyof Person returned to the name | address, But we can’t be sure it will be those two fields at run time.

Such as:

const obj2 = {
  name: 'itsuki'.address: 'hangzhou'.age: 20}; print(obj2)// compile time: ✅, because it has name, address attributes
// Runtime: ❌, since the age field is number, there is no toUpperCase method
Copy the code

Then I found this sentence in Github issue:

Types in TS are open-ended. Therefore, keysof may be less than all the attributes acquired at run time.

Keys () returns a string[] instead of a (keyof Person)[].