undefinedって2種類あんねん
要約
オブジェクトのプロパティに明示的にundefinedを指定するのと単にプロパティを省略するのでは挙動が異なるので注意しましょう。
本文
以下のようなオプショナルなプロパティをもつ型を定義したとします。
type SomeRecord = {
foo: number;
bar?: number;
};
さて、ここで以下のように変数を定義した時、x.bar
, y.bar
の値は同値になるでしょうか?
const hoge1: SomeRecord = {
foo: 1,
};
const hoge2: SomeRecord = {
foo: 1,
bar: undefined,
};
const fuga: SomeRecord = {
foo: 2,
bar: 3,
};
const x = { ...fuga, ...hoge1 };
const y = { ...fuga, ...hoge2 };
これは同値にはなりません。コードを実行するとx.bar
は3
になり、y.bar
はundefined
になります。
この挙動はオブジェクトが引数で指定したプロパティを持つか確認する.hasOwnProperty
メソッドを実行してみると謎が解けます。
追記)
コメントでpetamorikenさんから、
プロパティの存在チェックにはObject.hasOwn
が推奨されていると教えていただきました。Object.hasOwn
を使ってください。
hoge1.hasOwnProperty('bar') // false
hoge2.hasOwnProperty('bar') // true
ご覧の通り、hoge2
はbar
プロパティが存在するので、fuga.bar
が上書きされてしまうのでした。明示的にundefined
を指定しているので当たり前といわれたら確かにそうかもしれません。しかし、TypeScriptを使用する上でこれは結構好ましくない挙動だと私は思います。これはfuga
の型定義を修正すると何が不都合かすぐにわかります。
const fuga: Required<SomeRecord> = {
foo: 2,
bar: 3,
};
const x = { ...fuga, ...hoge1 };
const y = { ...fuga, ...hoge2 };
このようにbar
を必須プロパティにすると、x.bar
もy.bar
もnumber
型と推論されます。しかし、y.bar
はundefined
です。型と実際の値が食い違ってしまいます。TypeScriptの型を信じて安心していたらJavaScriptに背後から刺された気分です。
追記)
コメントでberlysiaさんから、
exactOptionalPropertyTypes
オプションを使えば、hoge2
を検出できると教えていただきました。
いかがでしたか?オブジェクトのプロパティに明示的にundefined
を指定するときは注意しましょう。その変数、本当に正しく型づけされていますか?
Discussion
exactOptionalPropertyTypes を有効にすると、
hoge2
のような記述を検出できそうですex. https://www.typescriptlang.org/play?exactOptionalPropertyTypes=true#code/C4TwDgpgBAyg9gWwgJQgYzgJwCZQLxQDeAUFFAGZxwBcUAdgK4IBGEmA3KVMwIaYD8tRizacAvp2IY6AZ2BQAFnADmEAIy14SVBhz4iXSjShqANMQnEpcWfKWqATJsQp0WXARJkjtM116YtAx02BDkAJZ0ENjmlta2FAzKPM7abnqehlS0DuZkAbQAzLGS0nJQAB76hFAAdPXkSTymdfX26lCWZfIg1a21jckt9bXtDp3sQA
ありがとうございます!!
後ほど追記させていただきます🙇♂️
そもそも明示的にundefinedを書くのがあまりよろしくないんですかね…
今ですと ES2022
Object.hasOwn
を使うほうが推奨されていますね。Safari が 15.4 からしか使えないので、対応ブラウザを考慮する必要はありますが……。undefinedって変数やねん
undefined 渡されたときに key ごと消したい場合は
Object.entries
を使ってキーと値で分離して filter かけたものをObject.fromEntries
に食わせると消せますね現状私もこれをやってますが、ランタイムに型チェックしてるようなものだし静的型付けとは…みたいな気分になってもやもやします🥲
よかった200種類なくて…
(すみません、ネタ元的に言いたくて仕方なかった)