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:
@@ -1,4 +1,6 @@
|
||||
import type { FunctionData, InstructionHolder } from '@/application/collections/';
|
||||
import type {
|
||||
FunctionData, CodeInstruction, CodeFunctionData, CallFunctionData, CallInstruction,
|
||||
} from '@/application/collections/';
|
||||
import { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { CodeValidator } from '@/application/Parser/Script/Validation/CodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmptyLines';
|
||||
@@ -23,9 +25,8 @@ export class SharedFunctionsParser implements ISharedFunctionsParser {
|
||||
functions: readonly FunctionData[],
|
||||
syntax: ILanguageSyntax,
|
||||
): ISharedFunctionCollection {
|
||||
if (!syntax) { throw new Error('missing syntax'); }
|
||||
const collection = new SharedFunctionCollection();
|
||||
if (!functions || !functions.length) {
|
||||
if (!functions.length) {
|
||||
return collection;
|
||||
}
|
||||
ensureValidFunctions(functions);
|
||||
@@ -55,16 +56,18 @@ function parseFunction(
|
||||
}
|
||||
|
||||
function validateCode(
|
||||
data: FunctionData,
|
||||
data: CodeFunctionData,
|
||||
syntax: ILanguageSyntax,
|
||||
validator: ICodeValidator,
|
||||
): void {
|
||||
[data.code, data.revertCode].forEach(
|
||||
(code) => validator.throwIfInvalid(
|
||||
code,
|
||||
[new NoEmptyLines(), new NoDuplicatedLines(syntax)],
|
||||
),
|
||||
);
|
||||
[data.code, data.revertCode]
|
||||
.filter((code): code is string => Boolean(code))
|
||||
.forEach(
|
||||
(code) => validator.throwIfInvalid(
|
||||
code,
|
||||
[new NoEmptyLines(), new NoDuplicatedLines(syntax)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
function parseParameters(data: FunctionData): IReadOnlyFunctionParameterCollection {
|
||||
@@ -85,19 +88,18 @@ function parseParameters(data: FunctionData): IReadOnlyFunctionParameterCollecti
|
||||
}, new FunctionParameterCollection());
|
||||
}
|
||||
|
||||
function hasCode(data: FunctionData): boolean {
|
||||
return Boolean(data.code);
|
||||
function hasCode(data: FunctionData): data is CodeFunctionData {
|
||||
return (data as CodeInstruction).code !== undefined;
|
||||
}
|
||||
|
||||
function hasCall(data: FunctionData): boolean {
|
||||
return Boolean(data.call);
|
||||
function hasCall(data: FunctionData): data is CallFunctionData {
|
||||
return (data as CallInstruction).call !== undefined;
|
||||
}
|
||||
|
||||
function ensureValidFunctions(functions: readonly FunctionData[]) {
|
||||
ensureNoUndefinedItem(functions);
|
||||
ensureNoDuplicatesInFunctionNames(functions);
|
||||
ensureNoDuplicateCode(functions);
|
||||
ensureEitherCallOrCodeIsDefined(functions);
|
||||
ensureNoDuplicateCode(functions);
|
||||
ensureExpectedParametersType(functions);
|
||||
}
|
||||
|
||||
@@ -105,7 +107,7 @@ function printList(list: readonly string[]): string {
|
||||
return `"${list.join('","')}"`;
|
||||
}
|
||||
|
||||
function ensureEitherCallOrCodeIsDefined(holders: readonly InstructionHolder[]) {
|
||||
function ensureEitherCallOrCodeIsDefined(holders: readonly FunctionData[]) {
|
||||
// Ensure functions do not define both call and code
|
||||
const withBothCallAndCode = holders.filter((holder) => hasCode(holder) && hasCall(holder));
|
||||
if (withBothCallAndCode.length) {
|
||||
@@ -132,7 +134,7 @@ function isArrayOfObjects(value: unknown): boolean {
|
||||
&& value.every((item) => typeof item === 'object');
|
||||
}
|
||||
|
||||
function printNames(holders: readonly InstructionHolder[]) {
|
||||
function printNames(holders: readonly FunctionData[]) {
|
||||
return printList(holders.map((holder) => holder.name));
|
||||
}
|
||||
|
||||
@@ -144,22 +146,19 @@ function ensureNoDuplicatesInFunctionNames(functions: readonly FunctionData[]) {
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNoUndefinedItem(functions: readonly FunctionData[]) {
|
||||
if (functions.some((func) => !func)) {
|
||||
throw new Error('some functions are undefined');
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNoDuplicateCode(functions: readonly FunctionData[]) {
|
||||
const duplicateCodes = getDuplicates(functions
|
||||
const callFunctions = functions
|
||||
.filter((func) => hasCode(func))
|
||||
.map((func) => func as CodeFunctionData);
|
||||
const duplicateCodes = getDuplicates(callFunctions
|
||||
.map((func) => func.code)
|
||||
.filter((code) => code));
|
||||
if (duplicateCodes.length > 0) {
|
||||
throw new Error(`duplicate "code" in functions: ${printList(duplicateCodes)}`);
|
||||
}
|
||||
const duplicateRevertCodes = getDuplicates(functions
|
||||
.filter((func) => func.revertCode)
|
||||
.map((func) => func.revertCode));
|
||||
const duplicateRevertCodes = getDuplicates(callFunctions
|
||||
.map((func) => func.revertCode)
|
||||
.filter((code): code is string => Boolean(code)));
|
||||
if (duplicateRevertCodes.length > 0) {
|
||||
throw new Error(`duplicate "revertCode" in functions: ${printList(duplicateRevertCodes)}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user