この単位では、JavaScript で不安になりやすい値、型、比較、型変換、truthy / falsy を扱う。
JavaScript は動的型付けの言語であり、値の型は実行時に決まる。
そのため、typeof、Array.isArray、===、Number.isNaN、?? などを使い分けて、値の状態を明示的に読む力が重要になる。
この単位は、TypeScript / React に進む前の土台としても重要な位置づけとなる。
props、state、API レスポンス、フォーム入力、初期値処理では、null、undefined、空文字、0、false、NaN などを扱う場面が多い。
この単位で扱う内容は次の通り。
- primitive
numberstringbooleannullundefinedbigintsymbol
- object
- 配列は object であること
- 関数も値であること
typeofArray.isArrayNaNInfinityNumber.isNaNNumber.isFiniteObject.isnullとundefined===!====!=- 暗黙の型変換
- 明示的な型変換
Boolean(value)Number(value)String(value)parseIntparseFloat
- truthy / falsy
||&&??- optional chaining
?. - nullish coalescing
??
この単位のファイル構成は次の通り。
src/
02-values-types-comparison/
index.js
primitive-values.js
object-and-function-values.js
type-inspection.js
null-and-undefined.js
number-special-values.js
object-is.js
strict-equality.js
loose-equality-and-coercion.js
explicit-conversion.js
truthy-falsy.js
logical-operators.js
optional-chaining.js
docs/
02-values-types-comparison.md
各ファイルの役割は次の通り。
index.js- Unit 02 のサンプルを順番に実行する入口。
primitive-values.js- primitive 値と、文字列などの immutable な性質を確認する。
object-and-function-values.js- object、array、function が値として扱えることを確認する。
type-inspection.jstypeofとArray.isArrayの使い分けを確認する。
null-and-undefined.jsnullとundefinedが現れる場面と default parameter の挙動を確認する。
number-special-values.jsNaN、Infinity、Number.isNaN、Number.isFiniteを確認する。
object-is.jsObject.isと===の違い、object の参照同一性を確認する。
strict-equality.js===/!==による厳密比較を確認する。
loose-equality-and-coercion.js==/!=と暗黙の型変換の注意点を確認する。
explicit-conversion.jsBoolean(value)、Number(value)、String(value)、parseInt、parseFloatを確認する。
truthy-falsy.js- truthy / falsy と条件式での値の扱いを確認する。
logical-operators.js||、&&、??による値の選択と default 値の違いを確認する。
optional-chaining.js- optional chaining
?.と nullish coalescing??の組み合わせを確認する。
- optional chaining
02-values-types-comparison.md- この単位の内容、実行方法、注目ポイント、確認観点をまとめたドキュメント。
Node.js 側のサンプルは、リポジトリ直下で次のコマンドを実行する。
npm run unit:02ESLint を確認する場合は次を実行する。
npm run lintPrettier の整形チェックを行う場合は次を実行する。
npm run format:check整形が必要な場合は次を実行する。
npm run formatコードは次の順番で読むと理解しやすい。
src/02-values-types-comparison/index.jssrc/02-values-types-comparison/primitive-values.jssrc/02-values-types-comparison/object-and-function-values.jssrc/02-values-types-comparison/type-inspection.jssrc/02-values-types-comparison/null-and-undefined.jssrc/02-values-types-comparison/number-special-values.jssrc/02-values-types-comparison/object-is.jssrc/02-values-types-comparison/strict-equality.jssrc/02-values-types-comparison/loose-equality-and-coercion.jssrc/02-values-types-comparison/explicit-conversion.jssrc/02-values-types-comparison/truthy-falsy.jssrc/02-values-types-comparison/logical-operators.jssrc/02-values-types-comparison/optional-chaining.js
前半で値の種類と型確認を押さえ、後半で比較、型変換、truthy / falsy、default 値の扱いを確認する。
index.js から読むと、Unit 全体の実行順を把握しやすい。
primitive-values.js では、JavaScript の primitive 値を並べて確認している。
const numberValue = 42;
const stringValue = 'JavaScript';
const booleanValue = true;
const nullValue = null;
const undefinedValue = undefined;
const bigintValue = 9007199254740993n;
const symbolValue = Symbol('unit-02');primitive 値は、値そのものとして扱う基本的な値となる。
一方、object-and-function-values.js では、object、array、function を扱っている。
const user = { id: 1, name: 'Alice', active: true };
const tags = ['javascript', 'nodejs', 'browser'];
const formatter = createDisplayName;JavaScript では function も値として扱える。
変数に代入したり、引数に渡したり、戻り値にしたりできる点は、後続の関数・コールバック・高階関数の理解につながる。
type-inspection.js では、typeof と Array.isArray の結果を並べて確認している。
const inspectionResults = values.map(({ label, value }) => ({
label,
typeofResult: typeof value,
isArray: Array.isArray(value),
}));typeof null は "object" になる。
配列も typeof では "object" になる。
console.log('typeof null:', typeof nullValue);
console.log('null === null:', nullValue === null);
console.log('typeof array:', typeof arrayValue);
console.log('Array.isArray(array):', Array.isArray(arrayValue));null を判定する場合は value === null を使う。
配列を判定する場合は Array.isArray(value) を使う。
null-and-undefined.js では、default parameter に undefined と null を渡したときの違いを確認している。
function createProfile({ nickname = '未設定' } = {}) {
return { nickname };
}const omittedProfile = createProfile();
const undefinedProfile = createProfile({ nickname: undefined });
const nullProfile = createProfile({ nickname: null });undefined は「値が渡されていない」扱いになり、既定値が使われる。
一方、null は値として明示的に渡されているため、既定値にはならない。
number-special-values.js では、NaN と Infinity を確認している。
const invalidNumber = Number('not-a-number');
const invalidCalculation = Math.sqrt(-1);
const positiveInfinity = 1 / 0;
const negativeInfinity = -1 / 0;NaN は number 型の特殊な値だが、通常の比較では自分自身とも等しくならない。
object-is.js では、=== と Object.is の違いを確認している。
const strictComparisonResults = {
nanStrictEqual: NaN === NaN,
zeroStrictEqual: 0 === -0,
};
const objectIsResults = {
nanObjectIs: Object.is(NaN, NaN),
zeroObjectIs: Object.is(0, -0),
sameString: Object.is('JavaScript', 'JavaScript'),
differentObjects: Object.is({ id: 1 }, { id: 1 }),
};Object.is(NaN, NaN) は true になる。
一方、Object.is(0, -0) は false になる。
strict-equality.js では、=== / !== を使った比較を確認している。
const sameNumber = 100 === 100;
const numberAndString = 100 === '100';
const trueAndOne = true === 1;
const nullAndUndefined = null === undefined;100 と '100' は、見た目は近くても型が違うため === では false になる。
一方、loose-equality-and-coercion.js では、== によって暗黙の型変換が起きる例を確認している。
const looseEqualityResults = {
numberAndString: 100 == '100',
falseAndZero: false == 0,
emptyStringAndZero: '' == 0,
nullAndUndefined: null == undefined,
zeroAndNull: 0 == null,
};このような比較は、結果だけ見ると便利に見えるが、読み手が変換規則を追う必要がある。
実務では、明示的に型変換したうえで === / !== を使う方が読みやすい。
logical-operators.js では、|| と ?? の違いを確認している。
const fallbackByOr = {
displayName: formValues.displayName || '匿名',
retryCount: formValues.retryCount || 3,
receiveMail: formValues.receiveMail || true,
};
const fallbackByNullish = {
displayName: formValues.displayName ?? '匿名',
retryCount: formValues.retryCount ?? 3,
receiveMail: formValues.receiveMail ?? true,
};|| は左辺が falsy の場合に右辺を返す。
そのため、空文字、0、false も default 値に置き換わる。
?? は左辺が null または undefined の場合だけ右辺を返す。
フォーム入力、設定値、API レスポンスなどで 0 や false を有効な値として扱う場合は ?? が向いている。
type-inspection.js では、null の typeof 結果を確認している。
const nullValue = null;
console.log('typeof null:', typeof nullValue);
console.log('null === null:', nullValue === null);typeof null は "object" になる。
これは JavaScript の歴史的な仕様として残っている挙動であり、null が通常の object という意味ではない。
null を判定したい場合は、value === null のように比較する。
配列も typeof では "object" になる。
const arrayValue = ['JavaScript', 'TypeScript'];
console.log('typeof array:', typeof arrayValue);
console.log('Array.isArray(array):', Array.isArray(arrayValue));配列かどうかを判定したい場合は、Array.isArray(value) を使う。
typeof だけで object と array を分けようとすると誤判定しやすい。
object-is.js では、NaN の比較を確認している。
const strictComparisonResults = {
nanStrictEqual: NaN === NaN,
zeroStrictEqual: 0 === -0,
};NaN は自分自身とも === で等しくならない。
NaN 判定には Number.isNaN(value) を使う。
Number.isNaN(invalidNumber);Number.isNaN は、値が本当に NaN かどうかを判定する。
文字列などを勝手に数値変換しないため、意図が読みやすい。
loose-equality-and-coercion.js では、== の挙動を確認している。
const looseEqualityResults = {
numberAndString: 100 == '100',
falseAndZero: false == 0,
emptyStringAndZero: '' == 0,
nullAndUndefined: null == undefined,
zeroAndNull: 0 == null,
};100 == '100' や false == 0 は true になる。
このような比較は暗黙の型変換に依存するため、コードを読む人が変換規則を追う必要がある。
基本的には、明示的に変換したうえで === を使う方がよい。
truthy-falsy.js では、空の配列や空の object が truthy になることを確認している。
const truthyValues = [
{ label: 'non-empty string', value: 'JavaScript' },
{ label: '1', value: 1 },
{ label: 'empty array', value: [] },
{ label: 'empty object', value: {} },
{ label: 'function', value: () => 'value' },
];空の配列や空の object は、中身が空でも Boolean(value) は true になる。
「空かどうか」を確認したい場合は、array.length === 0 や Object.keys(object).length === 0 のように別途確認する必要がある。
logical-operators.js では、|| と ?? の違いを確認している。
const formValues = {
displayName: '',
retryCount: 0,
receiveMail: false,
};const fallbackByOr = {
displayName: formValues.displayName || '匿名',
retryCount: formValues.retryCount || 3,
receiveMail: formValues.receiveMail || true,
};この場合、空文字、0、false はすべて default 値に置き換わる。
これらを有効な値として扱いたい場合は、?? を使う。
const fallbackByNullish = {
displayName: formValues.displayName ?? '匿名',
retryCount: formValues.retryCount ?? 3,
receiveMail: formValues.receiveMail ?? true,
};optional-chaining.js では、存在しないプロパティを安全に読む例を扱っている。
const missingCity = userWithoutProfile.profile?.address?.city;optional chaining は便利だが、必ず存在すべき値にまで付けると、設定漏れやデータ不整合に気づきにくくなる。
必須の設定は通常通り参照し、optional な値だけ ?. や ?? で扱う方がよい。
null-and-undefined.js では、API レスポンス風の object に null を含めている。
const userFromApi = {
id: 1,
name: 'Alice',
middleName: null,
};実務では、API レスポンスの中に null が含まれることがある。
一方で、JavaScript 側でプロパティが存在しない場合や、Map に key がない場合は undefined が返ることもある。
null と undefined のどちらが来る可能性があるかを理解しておくと、画面表示や変換処理を書きやすくなる。
explicit-conversion.js では、Number(value)、Boolean(value)、String(value) を扱っている。
const numberConversions = [
{ label: '"100"', converted: Number('100') },
{ label: '"10.5"', converted: Number('10.5') },
{ label: 'empty string', converted: Number('') },
{ label: '"text"', converted: Number('text') },
];フォーム入力値や URL query は、文字列として入ってくることが多い。
数値として扱いたい場合は、暗黙の型変換に任せず、Number(value) などで明示的に変換する方が読みやすい。
設定値やフォーム入力では、0、空文字、false が有効な値になることがある。
const formValues = {
displayName: '',
retryCount: 0,
receiveMail: false,
};このような値に default を入れる場合、|| を使うと意図せず値が置き換わる。
null / undefined のときだけ default を入れたい場合は、?? を使う。
optional-chaining.js では、ネストした object から値を取り出している。
const city = userResponse.profile?.address?.city;
const missingCity = userWithoutProfile.profile?.address?.city;API レスポンスや設定 object は、ネストしていることが多い。
途中の値が null / undefined の可能性がある場合、optional chaining を使うと安全に読み取れる。
ただし、必須の値にまで付けすぎると、データ不整合を見逃す可能性がある。
JavaScript では、null / undefined は実行時に初めて問題になることが多い。
TypeScript では、型として null / undefined の可能性を表現できる。
type User = {
id: number;
name: string;
middleName: string | null;
};JavaScript の段階で null / undefined の違いを理解しておくと、TypeScript の union 型や optional property を理解しやすくなる。
logical-operators.js では、|| と ?? の違いを確認した。
const fallbackByOr = {
displayName: formValues.displayName || '匿名',
retryCount: formValues.retryCount || 3,
receiveMail: formValues.receiveMail || true,
};
const fallbackByNullish = {
displayName: formValues.displayName ?? '匿名',
retryCount: formValues.retryCount ?? 3,
receiveMail: formValues.receiveMail ?? true,
};React の state や props では、0、空文字、false が有効な値になることがある。
その場合、|| で default 値を入れると、ユーザーの入力や設定を意図せず置き換える可能性がある。
React では、API 取得直後やロード中に値がまだ存在しないことがある。
その場合、optional chaining を使って安全に表示用の値を取り出すことがある。
function UserProfile({ user }) {
return <p>{user.profile?.address?.city ?? '未設定'}</p>;
}ただし、すべてに ?. を付けるのではなく、必須データと optional データを分けて考えることが重要になる。
JavaScript では、typeof や Array.isArray を使って実行時に値を判定する。
if (typeof value === 'string') {
return value.trim();
}
if (Array.isArray(value)) {
return value.length;
}TypeScript では、このような判定が型の絞り込みにもつながる。
function formatValue(value: string | string[]) {
if (Array.isArray(value)) {
return value.join(', ');
}
return value.trim();
}JavaScript で値を判定する書き方に慣れておくと、TypeScript の型ガードも理解しやすい。
この単位を読み終えたら、次を確認する。
- JavaScript の primitive 値を列挙できるか
- primitive と object の違いを説明できるか
- function も値として扱えることを説明できるか
- 配列が
typeofでは"object"になることを説明できるか - 配列判定に
Array.isArrayを使う理由を説明できるか typeof nullが"object"になることを知っているかnullとundefinedが現れやすい場面を説明できるかNaNが number 型の特殊な値であることを説明できるかNumber.isNaNとNumber.isFiniteの役割を説明できるかObject.isと===の違いを概要レベルで説明できるか- object の比較が中身ではなく参照の同一性を見ることを説明できるか
===/!==と==/!=の違いを説明できるか- 暗黙の型変換が起きる例を説明できるか
Boolean(value)、Number(value)、String(value)の役割を説明できるかparseInt/parseFloatとNumber(value)の違いを説明できるか- truthy / falsy の代表的な値を挙げられるか
- 空の配列や空の object が truthy になることを説明できるか
||と??の default 値処理の違いを説明できるか- optional chaining
?.を使う場面を説明できるか ?.を付けすぎると不具合を見逃しやすい理由を説明できるか