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:
@@ -8,7 +8,6 @@ export class Application implements IApplication {
|
||||
public info: IProjectInformation,
|
||||
public collections: readonly ICategoryCollection[],
|
||||
) {
|
||||
validateInformation(info);
|
||||
validateCollections(collections);
|
||||
}
|
||||
|
||||
@@ -16,19 +15,17 @@ export class Application implements IApplication {
|
||||
return this.collections.map((collection) => collection.os);
|
||||
}
|
||||
|
||||
public getCollection(operatingSystem: OperatingSystem): ICategoryCollection | undefined {
|
||||
return this.collections.find((collection) => collection.os === operatingSystem);
|
||||
}
|
||||
}
|
||||
|
||||
function validateInformation(info: IProjectInformation) {
|
||||
if (!info) {
|
||||
throw new Error('missing project information');
|
||||
public getCollection(operatingSystem: OperatingSystem): ICategoryCollection {
|
||||
const collection = this.collections.find((c) => c.os === operatingSystem);
|
||||
if (!collection) {
|
||||
throw new Error(`Operating system "${OperatingSystem[operatingSystem]}" is not defined in application`);
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
function validateCollections(collections: readonly ICategoryCollection[]) {
|
||||
if (!collections || !collections.length) {
|
||||
if (!collections.length) {
|
||||
throw new Error('missing collections');
|
||||
}
|
||||
if (collections.filter((c) => !c).length > 0) {
|
||||
|
||||
@@ -3,14 +3,14 @@ import { IScript } from './IScript';
|
||||
import { ICategory } from './ICategory';
|
||||
|
||||
export class Category extends BaseEntity<number> implements ICategory {
|
||||
private allSubScripts: ReadonlyArray<IScript> = undefined;
|
||||
private allSubScripts?: ReadonlyArray<IScript> = undefined;
|
||||
|
||||
constructor(
|
||||
id: number,
|
||||
public readonly name: string,
|
||||
public readonly docs: ReadonlyArray<string>,
|
||||
public readonly subCategories?: ReadonlyArray<ICategory>,
|
||||
public readonly scripts?: ReadonlyArray<IScript>,
|
||||
public readonly subCategories: ReadonlyArray<ICategory>,
|
||||
public readonly scripts: ReadonlyArray<IScript>,
|
||||
) {
|
||||
super(id);
|
||||
validateCategory(this);
|
||||
@@ -39,10 +39,7 @@ function validateCategory(category: ICategory) {
|
||||
if (!category.name) {
|
||||
throw new Error('missing name');
|
||||
}
|
||||
if (
|
||||
(!category.subCategories || category.subCategories.length === 0)
|
||||
&& (!category.scripts || category.scripts.length === 0)
|
||||
) {
|
||||
if (category.subCategories.length === 0 && category.scripts.length === 0) {
|
||||
throw new Error('A category must have at least one sub-category or script');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
public readonly actions: ReadonlyArray<ICategory>,
|
||||
public readonly scripting: IScriptingDefinition,
|
||||
) {
|
||||
if (!scripting) {
|
||||
throw new Error('missing scripting definition');
|
||||
}
|
||||
this.queryable = makeQueryable(actions);
|
||||
assertInRange(os, OperatingSystem);
|
||||
ensureValid(this.queryable);
|
||||
@@ -29,17 +26,26 @@ export class CategoryCollection implements ICategoryCollection {
|
||||
ensureNoDuplicates(this.queryable.allScripts);
|
||||
}
|
||||
|
||||
public findCategory(categoryId: number): ICategory | undefined {
|
||||
return this.queryable.allCategories.find((category) => category.id === categoryId);
|
||||
public getCategory(categoryId: number): ICategory {
|
||||
const category = this.queryable.allCategories.find((c) => c.id === categoryId);
|
||||
if (!category) {
|
||||
throw new Error(`Missing category with ID: "${categoryId}"`);
|
||||
}
|
||||
return category;
|
||||
}
|
||||
|
||||
public getScriptsByLevel(level: RecommendationLevel): readonly IScript[] {
|
||||
assertInRange(level, RecommendationLevel);
|
||||
return this.queryable.scriptsByLevel.get(level);
|
||||
const scripts = this.queryable.scriptsByLevel.get(level);
|
||||
return scripts ?? [];
|
||||
}
|
||||
|
||||
public findScript(scriptId: string): IScript | undefined {
|
||||
return this.queryable.allScripts.find((script) => script.id === scriptId);
|
||||
public getScript(scriptId: string): IScript {
|
||||
const script = this.queryable.allScripts.find((s) => s.id === scriptId);
|
||||
if (!script) {
|
||||
throw new Error(`missing script: ${scriptId}`);
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
public getAllScripts(): IScript[] {
|
||||
@@ -78,13 +84,13 @@ function ensureValid(application: IQueryableCollection) {
|
||||
}
|
||||
|
||||
function ensureValidCategories(allCategories: readonly ICategory[]) {
|
||||
if (!allCategories || allCategories.length === 0) {
|
||||
if (!allCategories.length) {
|
||||
throw new Error('must consist of at least one category');
|
||||
}
|
||||
}
|
||||
|
||||
function ensureValidScripts(allScripts: readonly IScript[]) {
|
||||
if (!allScripts || allScripts.length === 0) {
|
||||
if (!allScripts.length) {
|
||||
throw new Error('must consist of at least one script');
|
||||
}
|
||||
const missingRecommendationLevels = getEnumValues(RecommendationLevel)
|
||||
|
||||
@@ -7,5 +7,5 @@ export interface IApplication {
|
||||
readonly collections: readonly ICategoryCollection[];
|
||||
|
||||
getSupportedOsList(): OperatingSystem[];
|
||||
getCollection(operatingSystem: OperatingSystem): ICategoryCollection | undefined;
|
||||
getCollection(operatingSystem: OperatingSystem): ICategoryCollection;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import { IDocumentable } from './IDocumentable';
|
||||
export interface ICategory extends IEntity<number>, IDocumentable {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
readonly subCategories?: ReadonlyArray<ICategory>;
|
||||
readonly scripts?: ReadonlyArray<IScript>;
|
||||
readonly subCategories: ReadonlyArray<ICategory>;
|
||||
readonly scripts: ReadonlyArray<IScript>;
|
||||
includes(script: IScript): boolean;
|
||||
getAllScriptsRecursively(): ReadonlyArray<IScript>;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ export interface ICategoryCollection {
|
||||
readonly actions: ReadonlyArray<ICategory>;
|
||||
|
||||
getScriptsByLevel(level: RecommendationLevel): ReadonlyArray<IScript>;
|
||||
findCategory(categoryId: number): ICategory | undefined;
|
||||
findScript(scriptId: string): IScript | undefined;
|
||||
getCategory(categoryId: number): ICategory;
|
||||
getScript(scriptId: string): IScript;
|
||||
getAllScripts(): ReadonlyArray<IScript>;
|
||||
getAllCategories(): ReadonlyArray<ICategory>;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface IScriptCode {
|
||||
readonly execute: string;
|
||||
readonly revert: string;
|
||||
readonly revert?: string;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,6 @@ export class ProjectInformation implements IProjectInformation {
|
||||
if (!name) {
|
||||
throw new Error('name is undefined');
|
||||
}
|
||||
if (!version) {
|
||||
throw new Error('undefined version');
|
||||
}
|
||||
if (!slogan) {
|
||||
throw new Error('undefined slogan');
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ export class Script extends BaseEntity<string> implements IScript {
|
||||
public readonly level?: RecommendationLevel,
|
||||
) {
|
||||
super(name);
|
||||
if (!code) {
|
||||
throw new Error('missing code');
|
||||
}
|
||||
validateLevel(level);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@ import { IScriptCode } from './IScriptCode';
|
||||
export class ScriptCode implements IScriptCode {
|
||||
constructor(
|
||||
public readonly execute: string,
|
||||
public readonly revert: string,
|
||||
public readonly revert: string | undefined,
|
||||
) {
|
||||
validateCode(execute);
|
||||
validateRevertCode(revert, execute);
|
||||
}
|
||||
}
|
||||
|
||||
function validateRevertCode(revertCode: string, execute: string) {
|
||||
function validateRevertCode(revertCode: string | undefined, execute: string) {
|
||||
if (!revertCode) {
|
||||
return;
|
||||
}
|
||||
@@ -25,7 +25,7 @@ function validateRevertCode(revertCode: string, execute: string) {
|
||||
}
|
||||
|
||||
function validateCode(code: string): void {
|
||||
if (!code || code.length === 0) {
|
||||
if (code.length === 0) {
|
||||
throw new Error('missing code');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user