1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

keyofPropertyKey

keyof演算子はTypeScript の機能の1つで、オブジェクトのプロパティキーをユニオン型で取得できます。
このkeyofによって得られる型はすべてPropertyKey型の部分型、つまりstring | number | symbol型の部分型となります。

/lib.es5.d.ts
declare type PropertyKey = string | number | symbol;

オブジェクトのプロパティキーとしてsymbol型を使うこともできるわけですが、どのような用途があるのでしょうか。

この記事ではsymbol型のキーを持つオブジェクトについてみていきたいと思います。

symbol

symbolはプリミティブ値の1つで、「動的に一意の識別値を生成」「文字列への自動変換がされない」などの特徴を持ちます。

symbolの識別値はJavaScriptから直接アクセスや表示ができない内部的な値となっています。

オブジェクトとsymbol

symbolは一意の値となるため、オブジェクトのプロパティキーとして使用しても他のキーと競合しないというメリットがあります。

const key1 =  Symbol('foo')
const key2 =  Symbol('foo')

const obj = {
   [key1]: 123,
   [key2]: 234
}

console.log(obj[key1])
console.log(obj[key2])
//@ts-ignore
console.log(obj[Symbol('foo')])
123
234
undefined

プロパティキーの競合はしなくなるものの、可読性はあまり高くなく、普段の開発で見かけることはあまりなさそうですね。
主にライブラリやフレームワーク開発で活用されています。

symbolプロパティは列挙されない

オブジェクトにはObject.keys()Object.entries()といった列挙メソッドがあります。

「列挙可能な」プロパティキーや値を配列の形で返しますので、オブジェクトの各プロパティぞれぞれに処理を行いたいときなどに便利なメソッドです。

const fish = {
  tuna: "maguro",
  salmon: "sake",
  octopus: "tako",
}

console.log(Object.keys(fish))
// [ 'tuna', 'salmon', 'octopus' ]
console.log(Object.entries(fish))
// [ [ 'tuna', 'maguro' ], [ 'salmon', 'sake' ], [ 'octopus', 'tako' ] ]

symbol型のプロパティキーと列挙

symbol型のプロパティキーを持つオブジェクトに列挙メソッドを適用しても、symbol型のキーを持つプロパティは列挙されません。
symbol型のキーは、「列挙可能な」プロパティキーではないわけです。
ご指摘いただいた通り、列挙メソッドによって列挙されることと「列挙可能」であることは異なるものです。)

const squidSymbol = Symbol('squid')

const fish = {
  tuna: "maguro",
  salmon: "sake",
  [squidSymbol]: 'ika',
  octopus: "tako",
}

console.log(Object.keys(fish))
// [ 'tuna', 'salmon', 'octopus' ]
console.log(Object.entries(fish))
// [ [ 'tuna', 'maguro' ], [ 'salmon', 'sake' ], [ 'octopus', 'tako' ] ]

このプログラムにはsymbol型のキーをもつプロパティを追加していますが、コンソール上に表示される内容は先ほどのプログラムと同じものとなっています。

列挙させたくないプロパティをオブジェクトに持たせたいときには、symbol型のキーをもたせることで隠蔽できるわけですね。

ちなみに、Object.getOwnPropertyNames()というメソッドを使用すると「列挙不可能な」キーを含めて取得することができますが、このメソッドを使用した場合にも、symbol型のキーをもつプロパティは列挙されません。

const squidSymbol = Symbol('squid')

const fish = {
  tuna: "maguro",
  salmon: "sake",
  [squidSymbol]: 'ika',
  octopus: "tako",
}


console.log(Object.getOwnPropertyNames(fish))
// [ 'tuna', 'salmon', 'octopus' ]

symbol型のキーを列挙するには

symbol型のキーはあらゆるメソッドから隠蔽されるわけではなく、Reflect.ownKeys()などを使うと確認することができます。

const squidSymbol = Symbol('squid')

const fish = {
  tuna: "maguro",
  salmon: "sake",
  [squidSymbol]: 'ika',
  octopus: "tako",
}

Object.defineProperty(fish, 'crub', {
    value: 'kani'
  });
  

console.log(Object.keys(fish))
// [ 'tuna', 'salmon', 'octopus' ]
console.log(Object.getOwnPropertyNames(fish))
// [ 'tuna', 'salmon', 'octopus', 'crub' ]
console.log(Reflect.ownKeys(fish))
// [ 'tuna', 'salmon', 'octopus', 'crub', Symbol(squid) ]

このプログラムでは、キーがsymbol型ではない列挙不可能なプロパティをObject.defineProperty()によって追加しています1

symbol型のキーを使用したとしてもプロパティを完全に隠蔽できるわけではないため、ライブラリやフレームワーク開発など内部のみでしか使用しないプロパティが出てくる場面では有用ですが、セキュリティの向上を目的に使用することにはあまり適していなさそうですね。

  1. defineProperty()enumerableのデフォルト値はfalseなので、このメソッドでプロパティを追加するだけで列挙不可能なプロパティとなります。

1
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?