Refactor to enforce strictNullChecks

This commit applies `strictNullChecks` to the entire codebase to improve
maintainability and type safety. Key changes include:

- Remove some explicit null-checks where unnecessary.
- Add necessary null-checks.
- Refactor static factory functions for a more functional approach.
- Improve some test names and contexts for better debugging.
- Add unit tests for any additional logic introduced.
- Refactor `createPositionFromRegexFullMatch` to its own function as the
  logic is reused.
- Prefer `find` prefix on functions that may return `undefined` and
  `get` prefix for those that always return a value.
This commit is contained in:
undergroundwires
2023-11-12 22:54:00 +01:00
parent 7ab16ecccb
commit 949fac1a7c
294 changed files with 2477 additions and 2738 deletions

View File

@@ -1,9 +1,6 @@
import { IApplication } from '@/domain/IApplication';
export function useApplication(application: IApplication) {
if (!application) {
throw new Error('missing application');
}
return {
application,
info: application.info,

View File

@@ -7,13 +7,6 @@ export function useCollectionState(
context: IApplicationContext,
events: IEventSubscriptionCollection,
) {
if (!context) {
throw new Error('missing context');
}
if (!events) {
throw new Error('missing events');
}
const currentState = shallowRef<IReadOnlyCategoryCollectionState>(context.state);
events.register([
context.contextChanged.on((event) => {
@@ -28,9 +21,6 @@ export function useCollectionState(
handler: NewStateEventHandler,
settings: Partial<IStateCallbackSettings> = defaultSettings,
) {
if (!handler) {
throw new Error('missing state handler');
}
events.register([
context.contextChanged.on((event) => {
handler(event.newState, event.oldState);
@@ -46,16 +36,10 @@ export function useCollectionState(
}
function modifyCurrentState(mutator: StateModifier) {
if (!mutator) {
throw new Error('missing state mutator');
}
mutator(context.state);
}
function modifyCurrentContext(mutator: ContextModifier) {
if (!mutator) {
throw new Error('missing context mutator');
}
mutator(context);
}

View File

@@ -1,8 +1,5 @@
import { IRuntimeEnvironment } from '@/infrastructure/RuntimeEnvironment/IRuntimeEnvironment';
export function useRuntimeEnvironment(environment: IRuntimeEnvironment) {
if (!environment) {
throw new Error('missing environment');
}
return environment;
}

View File

@@ -62,7 +62,7 @@ const RawSvgLoaders = import.meta.glob('@/presentation/assets/icons/**/*.svg', {
});
function modifySvg(svgSource: string): string {
const parser = new window.DOMParser();
const parser = new globalThis.window.DOMParser();
const doc = parser.parseFromString(svgSource, 'image/svg+xml');
let svgRoot = doc.documentElement;
svgRoot = removeSvgComments(svgRoot);

View File

@@ -4,7 +4,7 @@ import { Ref, watchEffect } from 'vue';
* Manages focus transitions, ensuring good usability and accessibility.
*/
export function useCurrentFocusToggle(shouldDisableFocus: Ref<boolean>) {
let previouslyFocusedElement: HTMLElement | undefined;
let previouslyFocusedElement: HTMLElement | null;
watchEffect(() => {
if (shouldDisableFocus.value) {
@@ -17,7 +17,7 @@ export function useCurrentFocusToggle(shouldDisableFocus: Ref<boolean>) {
return;
}
previouslyFocusedElement.focus();
previouslyFocusedElement = undefined;
previouslyFocusedElement = null;
}
});
}

View File

@@ -6,7 +6,7 @@
<script lang="ts">
import {
defineComponent, shallowRef, onMounted, onBeforeUnmount,
defineComponent, shallowRef, onMounted, onBeforeUnmount, watch,
} from 'vue';
import { useResizeObserverPolyfill } from '@/presentation/components/Shared/Hooks/UseResizeObserverPolyfill';
@@ -25,61 +25,63 @@ export default defineComponent({
let width = 0;
let height = 0;
let observer: ResizeObserver;
let observer: ResizeObserver | undefined;
onMounted(() => {
width = containerElement.value.offsetWidth;
height = containerElement.value.offsetHeight;
resizeObserverReady.then(() => {
observer = new ResizeObserver(updateSize);
observer.observe(containerElement.value);
});
fireChangeEvents();
watch(() => containerElement.value, async (element) => {
if (!element) {
disposeObserver();
return;
}
resizeObserverReady.then(() => {
observer = new ResizeObserver(updateSize);
observer.observe(element);
});
updateSize();
}, { immediate: true });
});
onBeforeUnmount(() => {
observer?.disconnect();
disposeObserver();
});
function updateSize() {
let sizeChanged = false;
if (isWidthChanged()) {
updateWidth(containerElement.value.offsetWidth);
sizeChanged = true;
}
if (isHeightChanged()) {
updateHeight(containerElement.value.offsetHeight);
sizeChanged = true;
}
if (sizeChanged) {
const changes = [
updateWidth(),
updateHeight(),
];
if (changes.some((c) => c.isChanged)) {
emit('sizeChanged');
}
}
function updateWidth(newWidth: number) {
function updateWidth(): {
readonly isChanged: boolean;
} {
const newWidth = containerElement.value?.offsetWidth ?? 0;
if (newWidth === width) {
return { isChanged: false };
}
width = newWidth;
emit('widthChanged', newWidth);
return { isChanged: true };
}
function updateHeight(newHeight: number) {
function updateHeight(): {
readonly isChanged: boolean;
} {
const newHeight = containerElement.value?.offsetHeight ?? 0;
if (newHeight === height) {
return { isChanged: false };
}
height = newHeight;
emit('heightChanged', newHeight);
return { isChanged: true };
}
function fireChangeEvents() {
updateWidth(containerElement.value.offsetWidth);
updateHeight(containerElement.value.offsetHeight);
emit('sizeChanged');
}
function isWidthChanged(): boolean {
return width !== containerElement.value.offsetWidth;
}
function isHeightChanged(): boolean {
return height !== containerElement.value.offsetHeight;
function disposeObserver() {
observer?.disconnect();
observer = undefined;
}
return {

View File

@@ -10,11 +10,11 @@ export function throttle(
}
// Allows aligning with both NodeJs (NodeJs.Timeout) and Window type (number)
export type TimeoutType = ReturnType<typeof setTimeout>;
export type Timeout = ReturnType<typeof setTimeout>;
export interface ITimer {
setTimeout: (callback: () => void, ms: number) => TimeoutType;
clearTimeout: (timeoutId: TimeoutType) => void;
setTimeout: (callback: () => void, ms: number) => Timeout;
clearTimeout: (timeoutId: Timeout) => void;
dateNow(): number;
}
@@ -29,7 +29,7 @@ interface IThrottler {
}
class Throttler implements IThrottler {
private queuedExecutionId: TimeoutType;
private queuedExecutionId: Timeout | undefined;
private previouslyRun: number;
@@ -38,10 +38,8 @@ class Throttler implements IThrottler {
private readonly waitInMs: number,
private readonly callback: CallbackType,
) {
if (!timer) { throw new Error('missing timer'); }
if (!waitInMs) { throw new Error('missing delay'); }
if (waitInMs < 0) { throw new Error('negative delay'); }
if (!callback) { throw new Error('missing callback'); }
}
public invoke(...args: unknown[]): void {