Ban the null literal via ESLint and replace existing usages with undefined or truthiness checks (#4401)

This commit is contained in:
Keavon Chambers
2026-08-03 23:47:29 -07:00
committed by Dennis Kobert
parent 4f318bbdae
commit 6cb4490137
4 changed files with 17 additions and 11 deletions

View File

@@ -73,6 +73,13 @@ export default defineConfig([
],
},
],
"no-restricted-syntax": [
"error",
{
selector: "Literal[raw='null']",
message: "Use `undefined` instead of `null`.",
},
],
// TypeScript plugin config (for TS-specific linting)
"@typescript-eslint/indent": "off",
@@ -96,7 +103,7 @@ export default defineConfig([
"@typescript-eslint/consistent-type-assertions": ["error", { assertionStyle: "never" }],
"@typescript-eslint/consistent-indexed-object-style": ["error", "record"],
"@typescript-eslint/consistent-generic-constructors": ["error", "constructor"],
"@typescript-eslint/no-restricted-types": ["error", { types: { null: "Use `undefined` instead." } }],
"@typescript-eslint/no-restricted-types": ["error", { types: { null: "Use `undefined` instead of `null`." } }],
// Prettier plugin config (for validating and fixing formatting)
"prettier/prettier": "error",

View File

@@ -28,7 +28,7 @@ export function createSRgba8FromHsva(h: number, s: number, v: number, a: number)
// COLOR UTILITY FUNCTIONS
export function isSRgba8(value: unknown): value is SRGBA8 {
return typeof value === "object" && value !== null && "red" in value;
return !!value && typeof value === "object" && "red" in value;
}
// Parse a CSS color string into an `SRGBA8`. Uses a canvas to delegate parsing to the browser.
@@ -183,11 +183,11 @@ export function contrastingOutlineFactor(value: FillChoice<SRGBA8>, proximityCol
// GRADIENT UTILITY FUNCTIONS
export function isGradientStops(value: unknown): value is GradientStops<SRGBA8> {
return typeof value === "object" && value !== null && "color" in value && Array.isArray(value.color);
return !!value && typeof value === "object" && "color" in value && Array.isArray(value.color);
}
export function isGradientRamp(value: unknown): value is GradientRamp<SRGBA8> {
return typeof value === "object" && value !== null && "stops" in value && isGradientStops(value.stops);
return !!value && typeof value === "object" && "stops" in value && isGradientStops(value.stops);
}
// FILL CHOICE UTILITY FUNCTIONS
@@ -203,8 +203,7 @@ export function fillChoiceGradient(value: FillChoice<SRGBA8>): GradientStops<SRG
}
export function parseFillChoice(value: unknown): FillChoice<SRGBA8> {
if (value === "None" || value === undefined || value === null) return "None";
if (typeof value === "object" && value !== null && "Solid" in value && isSRgba8(value.Solid)) return { Solid: value.Solid };
if (typeof value === "object" && value !== null && "Gradient" in value && isGradientRamp(value.Gradient)) return { Gradient: value.Gradient };
if (value && typeof value === "object" && "Solid" in value && isSRgba8(value.Solid)) return { Solid: value.Solid };
if (value && typeof value === "object" && "Gradient" in value && isGradientRamp(value.Gradient)) return { Gradient: value.Gradient };
return "None";
}

View File

@@ -353,7 +353,7 @@ function targetIsTextField(target: EventTarget | HTMLElement | undefined): boole
function potentiallyRestoreCanvasFocus(e: Event) {
const appElement = window.document.querySelector("[data-app-container]");
const app = appElement instanceof HTMLElement ? appElement : null;
const app = appElement instanceof HTMLElement ? appElement : undefined;
const newInCanvasArea =
(e.target instanceof Element && e.target.closest("[data-viewport], [data-viewport-container], [data-graph]")) instanceof Element &&

View File

@@ -147,7 +147,7 @@ async function migrateToNewFormat() {
if (oldDocuments) {
Object.values(oldDocuments).forEach((value) => {
const oldEntry: unknown = value;
if (typeof oldEntry !== "object" || oldEntry === null) return;
if (!oldEntry || typeof oldEntry !== "object") return;
if (!("documentId" in oldEntry) || !("document" in oldEntry) || !("details" in oldEntry)) return;
// Extract the document ID, handling bigint, number, and string formats
@@ -168,7 +168,7 @@ async function migrateToNewFormat() {
// Extract document details, handling camelCase from the old shipped format
const details: unknown = oldEntry.details;
if (typeof details !== "object" || details === null) return;
if (!details || typeof details !== "object") return;
let name = "";
if ("name" in details && typeof details.name === "string") name = details.name;
@@ -193,7 +193,7 @@ async function migrateToNewFormat() {
// TODO: Eventually remove this document upgrade code
function extractIsSavedFromUnknown(details: unknown): boolean {
if (typeof details !== "object" || details === null) return false;
if (!details || typeof details !== "object") return false;
// Old camelCase format
if ("isSaved" in details) return Boolean(details.isSaved);