mochikuni
@mochikuni (Mochikuni Mano)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

with文の代わりにオブジェクトを省略する記法はありますか?

Q&A

Closed

解決したいこと

JavaScriptでTypeScriptのような型注釈を行なうプログラムを作っています。
TypeAnnotationオブジェクトに各型注釈をObject.defineProperty()で実装していますが、
一々TypeAnnotationと記述するのが面倒でwith(TypeAnnotation) {...}のように書いています。
このwith文は非推奨な上に複雑なバグや互換性の問題があるようです。

下記コードは動作確認用のサンプルで、実際のコードでは、TypeAnnotationは外部ファイル(.cjs)から読み込むようにしているので、
taなどのように短縮してwithを使用しないで記述しても良いのですが、
他に何か方法はありますでしょうか?

TypeScriptを使う以外でお願いします。

該当するソースコード

const TypeAnnotation = {};

Object.defineProperty(TypeAnnotation, "number", {
  get() {
    return 0;
  },

  set(value) {
    if(typeof value !== "number")
      throw new TypeError(`'${typeof value}' is not a 'number'`);
  }
});

class Point {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

try {
  let props = null;
  with(TypeAnnotation) {
    props = {
      num: number = 1,
      ary: [number, number, number] = [0, 1, 2],
      pnt: {
        x: number,
        y: number
      } = new Point(100, 100)
    };
  }

  with(props) {
    console.table(num);
    console.table(ary);
    console.table(pnt);
  }
} catch(e) {
  console.log(e.message);
}

1
┌─────────┬────────┐
│ (index) │ Values │
├─────────┼────────┤
│    0    │   0    │
│    1    │   1    │
│    2    │   2    │
└─────────┴────────┘
┌─────────┬────────┐
│ (index) │ Values │
├─────────┼────────┤
│    x    │  100   │
│    y    │  100   │
└─────────┴────────┘

自分で試したこと

プロパティを使用しているコードを関数で囲って、
call(オブジェクト)で呼んだりしましたが、
関数内で頭にthis.をつけないと利用出来ませんでした。

const TypeAnnotation = {};

Object.defineProperty(TypeAnnotation, "number", {
  get() {
    return 0;
  },

  set(value) {
    if(typeof value !== "number")
      throw new TypeError(`'${typeof value}' is not a 'number'`);
  }
});

class Point {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }
}

try {
  let props = null;
  (function() {
    props = {
      num: this.number,
      ary: [this.number, this.number, this.number] = [3, 4, 5],
      pnt: {
        x: this.number,
        y: this.number
      } = new Point()
    };
  }).call(TypeAnnotation);

  (function() {
    console.table(this.num);
    console.table(this.ary);
    console.table(this.pnt);
  }).call(props);
} catch(e) {
  console.log(e.message);
}

0
┌─────────┬────────┐
│ (index) │ Values │
├─────────┼────────┤
│    0    │   3    │
│    1    │   4    │
│    2    │   5    │
└─────────┴────────┘
┌─────────┬────────┐
│ (index) │ Values │
├─────────┼────────┤
│    x    │   0    │
│    y    │   0    │
└─────────┴────────┘
0

1Answer

withはスコープがわからなくなるなどデメリットが多いですが
小規模な関数内で正確に動作しているのであれば気にすることはないと思います

他の言語ではそのオブジェクト名が長いだけなので短い名称にしたオブジェクトに参照代入して使うのですが
今度はjavascriptの参照代入が特殊なのでお薦めできません

withが必要なぐらいそのオブジェクトに依存した処理であれば
そのオブジェクトを継承してオブジェクトのメソッド内に処理を書くというのはどうでしょうか?

1Like

Comments

  1. @mochikuni

    Questioner

    回答ありがとうございます。

    withはスコープがわからなくなるなどデメリットが多いですが

    確かにスコープがわからなくなりそうですね。
    そういえばPointクラスをTypeAnnotationに追加して、
    プロパティを作成しようと以下のよう書いた際に、

    
     // 〜 省略 〜
    
      let prop = null;
      with(TypeAnnotation) {
        prop = {
          pnt: Point = new Point() // Point is not a constructor
        };
      }
    

    Pointはコンストラクタではないの様な主旨のエラーが出ていたのを思い出しました。
    スコープ内でnewしなければ問題はないと思います。
    (withを使用せずにTypeAnnotation.Pointや
    関数で囲ってcall(TypeAnnotation)で内部でthis.Pointならエラーになりませんでした)

    今度はjavascriptの参照代入が特殊なのでお薦めできません

    Qiitaに参照関連の記事があるのは知っていますが
    なかなか理解出来て無いです。

    withが必要なぐらいそのオブジェクトに依存した処理であれば
    そのオブジェクトを継承してオブジェクトのメソッド内に処理を書くというのはどうでしょうか?

    無い知恵を絞って考えてみましたがこういう事でしょうか?

    const TypeAnnotation = {};
    
    Object.defineProperty(TypeAnnotation, "number", {
      get() {
        return 0;
      },
    
      set(value) {
        if(typeof value !== "number")
          throw new TypeError(`'${typeof value}' is not a 'number'`);
      }
    });
    
    try {
      const init = Object.create(TypeAnnotation);
      init.method = function() {
        return {
          num: this.number = 2
        };
      };
    
      const main = Object.create(init.method());
      main.method = function() {
        console.log(this.num);
      };
    
      main.method();
    } catch(e) {
      console.log(e.message);
    }
    

  2. @mochikuni

    Questioner

    プロトタイプベースで別の実装を書いてみました。

    const TypeAnnotation = {};
    
    Object.defineProperty(TypeAnnotation, "number", {
      get() {
        return 0;
      },
    
      set(value) {
        if(typeof value !== "number")
          throw new TypeError(`'${typeof value}' is not a 'number'`);
      }
    });
    
    try {
      function Prop() {
        Object.assign(this, {
          num: this.number = 3
        });
      }
      Prop.prototype = Object.create(TypeAnnotation);
    
      function Main() {
        Prop.call(this);
        console.log(this.num); // 3
      }
      Main.prototype = Object.create(Prop.prototype);
    
      new Main();
    } catch(e) {
      console.log(e.message);
    }
    

    勉強になります。
    一旦クローズとさせて頂きます。
    ありがとうございました!

Your answer might help someone💌