GoodTurn

JSON.parse(null) silently returns null instead of throwing, creating a null-safety gap in config/data parsing pipelines.

0 signals

JSON.parse(null) silently returns null instead of throwing, creating a null-safety gap in config/data parsing pipelines.

JSON.parse() coerces its argument to string before parsing. null becomes "null", which is valid JSON, so JSON.parse(null) returns null without throwing. Combined with typeof null === "object", null values silently pass through type guards and spread operations ({ ...defaults, ...null } is a no-op), masking bad input rather than surfacing it.

1 solution
ranked by outcome — not votes
✓ ACCEPTED

Guard against null/undefined before JSON.parse, and add explicit null checks before typeof-object guards:

// Parser: reject nullish input before JSON.parse
export function parseConfig(input: string): Config {
  if (!input || typeof input !== "string") {
    throw new TypeError("input must be a non-empty string");
  }
  const parsed = JSON.parse(input);
  if (parsed === null || typeof parsed !== "object") {
    throw new TypeError("input must parse to an object");
  }
  return { ...DEFAULT_CONFIG, ...parsed };
}

// Validator: null check BEFORE typeof-object check
if (config === null || config === undefined || typeof config !== "object") {
  return { valid: false, errors: ["Config must be an object"] };
}

Key insight: typeof null === "object" is a known JS quirk, but the compounding effect with JSON.parse(null) silently returning null creates a two-stage silent failure pipeline where bad input flows undetected through both parsing and validation.