Object.keys()の返り値に謎の数字の配列が返ってきたときの対処

2021/04/16に公開

はじめに

需要があるかわからないがトラブルシューティングとして残しておく。
かなり単純なミスだった。

再現

jsonファイルを読み込み、そのkeyをすべて取り出そうとした。
色々書いてあるが要は"dev"と"production"というキーを取り出せればよかった。

{
  "dev": {
    "awscloudformation": {
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    },
    "prod":{
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    }
  },
  "production": {
    "awscloudformation": {
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    },
    "prod":{
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    }
  }
}

デバックのため以下のようなコードを書き、consoleに表示するとObject.keysから謎の配列が返ってきた。

const content = fs.readFileSync(path, "utf8");
consoe.log(content)
console.log(Object.keys(content));

// consoe.log(content)
{
  "dev": {
    "awscloudformation": {
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    },
    "prod":{
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    }
  },
  "production": {
    "awscloudformation": {
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    },
    "prod":{
      "AuthRoleName": "文字列が入ります",
      "UnauthRoleArn": "文字列が入ります",
      "AuthRoleArn": "文字列が入ります",
      "Region": "文字列が入ります",
      "DeploymentBucketName": "文字列が入ります",
      "UnauthRoleName": "文字列が入ります",
      "StackName": "文字列が入ります",
      "StackId": "文字列が入ります",
      "AmplifyAppId": "文字列が入ります"
    }
  }
}
//consoe.log(Object.keys(content))
[
  '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',
  '10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
  '20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
  '30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
  '40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
  '50', '51', '52', '53', '54', '55', '56', '57', '58', '59',
  '60', '61', '62', '63', '64', '65', '66', '67', '68', '69',
  '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
  '80', '81', '82', '83', '84', '85', '86', '87', '88', '89',
  '90', '91', '92', '93', '94', '95', '96', '97', '98', '99',
  ... 1251 more items
]

原因と対処

fs.readFileSyncの型定義ファイルを確認してみる。返り値の型に注目してほしい。

export function readFileSync(path: PathLike | number, options: { encoding: BufferEncoding; flag?: string; } | BufferEncoding): string;

あくまでfs.readFileSyncの返り値はただの"string"型である。
console.log(content)で表示したものはパット見オブジェクトに見えたが実態はただの文字列である。
すなわち、原因は単に引数にobjectを入れるべきところにstringを突っ込んでいたために起きた。
謎の数字の配列の正体はただのindex番号であった。(stringを配列のようなオブジェクトとして読み込みこんでしまっていた。詳しくはこちら)

そのため対処は単純でparseして上げればよい。

// string -> objectへ変換
const parsedContent = JSON.parse(content)

して

Object.keys(parsedContent)

すれば正しくオブジェクトのkeyを取り出せる。

Discussion