0
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?

[Node.js] TS2809とESLint Semiなしルールがぶつかって変な挙動した

Posted at
スクリプト1
const obj = {
    a: 123,
    b: "abc"
}

let a = () => {} // 文1: とりあえず空の関数で変数初期化しているだけね
let b = () => {} // 文2: 同上

({a, b} = obj)

console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
a 123
b abc

正しく呼べているように見える

スクリプト2
const obj = {
    a: 123,
    b: "abc"
}

let a = null // 文3: とりあえず空の関数で変数初期化しているだけね
let b = null // 文4: 同上

({a, b} = obj)

console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
[stdin]:9
({a, b} = obj)
     ^

ReferenceError: Cannot access 'b' before initialization
    at [stdin]:9:6
    at Script.runInThisContext (node:vm:129:12)
    at Object.runInThisContext (node:vm:307:38)
    at node:internal/process/execution:79:19
    at [stdin]-wrapper:6:22
    at evalScript (node:internal/process/execution:78:60)
    at node:internal/main/eval_stdin:30:5
    at Socket.<anonymous> (node:internal/process/execution:195:5)
    at Socket.emit (node:events:525:35)
    at endReadableNT (node:internal/streams/readable:1359:12)

Node.js v18.16.0

変数初期化の値を変えるだけで動かなくなるの?謎。

スクリプト3
const obj = {
    a: 123,
    b: "abc"
}

let a = null
let b = null
b = null

({a, b} = obj)

console.log("a", a)
console.log("b", b)
結果
% pbpaste | node
[stdin]:10
({a, b} = obj)
^

TypeError: null is not a function
    at [stdin]:10:1
    at Script.runInThisContext (node:vm:129:12)
    at Object.runInThisContext (node:vm:307:38)
    at node:internal/process/execution:79:19
    at [stdin]-wrapper:6:22
    at evalScript (node:internal/process/execution:78:60)
    at node:internal/main/eval_stdin:30:5
    at Socket.<anonymous> (node:internal/process/execution:195:5)
    at Socket.emit (node:events:525:35)
    at endReadableNT (node:internal/streams/readable:1359:12)

Node.js v18.16.0

これでもダメか・・・

答え合わせ

  • セミコロンを置いていないので、スクリプト2では文4の後の文のカッコが関数呼び出しとみなされている
    • その結果 null({a, b} = obj) という関数呼び出しのような形になった
    • カッコの中身が null の次に評価されて、それが b に代入されるという形になった
    • カッコの中身の評価中に b が必要だがこれはまさに今宣言しようとしている値なのでinitialize前に使うなというエラーが出た
  • 一方でスクリプト1では構文解析器が文2の後に自動的にセミコロンを置いてくれた

分割代入にカッコが必要な理由

TS2809による

変数の分割代入を行い、かつその場で変数・定数宣言しない場合(つまりletやconstを使わずに分割代入する場合)は代入文にカッコが必要

let {a, b} = obj; // これはOK
{a, b} = obj; // これはNG
({a, b} = obj); // これはOK

セミコロンを置かなかった理由

ESLintのルールで、このプロジェクトではセミコロンありだと警告が出るようになっていた


上記2つが悪魔合体した結果、スクリプト2のようにぱっと見動作しそうだがなぜかエラーになる・スクリプト1のケースではなぜか動く という奇妙な状態になっていた

面白い挙動だったので備忘録として残しておく

0
0
0

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
0
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?