Skip to content

Commit a832c7e

Browse files
committed
fix: introduce proper typing for json objects.
- Added proper typings for json objects. - `JsonObject` interface to define a generic json object. - `JsonValue` type to define the possible types of the json values. - `JsonArray` type to define an array in json. - Added interfaces to define the single-line and multi-line language definitions, which extend the `JsonObject`: `SingleLineLanguageDefinitions` and `MultiLineLanguageDefinitions`. - Refactored `readJsonFile` util function to use the new `JsonObject` interface and `JsonValue` type. The function now passes a generic type `T` as it's return value, if the generic type isn't defined when calling the function, then it defaults to `JsonObject`. This ensures that specific types or interfaces can be defined as the return type that naturally is or extends a `JsonObject`. Eg. `readJsonFile<IPackageJson>()`. - Refactored `writeJsonFile` util function to pass a generic type `T` as it's return value and it's `data` param type as `T`. - Changed `obj` param type in `reconstructRegex` util function to `unknown. - Changed `convertMapToReversedObject` util function return type to `JsonObject`. - Changed `readJsonFile` function call in `ExtensionData::getExtensionPackageJsonData` method to define the generic return type as `IPackageJson`. - Changed `Configuration::getLanguagesToSkip` method return type to `JsonArray`. - Refactored `Configuration::writeCommentLanguageDefinitionsToJsonFile` method to properly type the code as the new `MultiLineLanguageDefinitions` and `SingleLineLanguageDefinitions` interfaces. - Changed `defaultMultiLineConfig` variable in `Configuration::setLanguageConfiguration` to be cast as `vscode.LanguageConfiguration` to ensure the `readJsonFile` return type is of that type. - Auto-formatting changes., like removal of commas. - Changed the `readJsonFile` function call return type in `Configuration::logDebugInfo` method to use the `MultiLineLanguageDefinitions` and `SingleLineLanguageDefinitions` interfaces.
1 parent 4c4e361 commit a832c7e

4 files changed

Lines changed: 73 additions & 23 deletions

File tree

src/configuration.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {ExtensionData} from "./extensionData";
1414
import {Settings} from "./interfaces/settings";
1515
import {LineComment, SingleLineCommentStyle} from "./interfaces/commentStyles";
1616
import {ExtensionMetaData} from "./interfaces/extensionMetaData";
17+
import {MultiLineLanguageDefinitions, SingleLineLanguageDefinitions} from "./interfaces/utils";
18+
import {JsonArray} from "./interfaces/utils";
1719

1820
export class Configuration {
1921
/**************
@@ -167,7 +169,7 @@ export class Configuration {
167169
"auto-comment-blocks.changeBladeMultiLineBlock",
168170
(textEditor, edit, args) => {
169171
this.handleChangeBladeMultiLineBlock(textEditor);
170-
},
172+
}
171173
);
172174
return [singleLineBlockCommand, changeBladeMultiLineBlockCommand];
173175
}
@@ -294,11 +296,11 @@ export class Configuration {
294296
*
295297
* Idea from this StackOverflow answer https://stackoverflow.com/a/72988011/2358222
296298
*
297-
* @returns {string[]}
299+
* @returns {JsonArray}
298300
*/
299-
private getLanguagesToSkip(): string[] {
301+
private getLanguagesToSkip(): JsonArray {
300302
const json = utils.readJsonFile(`${__dirname}/../../config/skip-languages.jsonc`);
301-
return json.languages;
303+
return json.languages as JsonArray;
302304
}
303305

304306
/**
@@ -671,10 +673,16 @@ export class Configuration {
671673
// Ensure the auto-generated directory exists.
672674
utils.ensureDirExists(this.autoGeneratedDir);
673675

674-
// Write the into the single-line-languages.json file.
675-
utils.writeJsonFile(this.singleLineLangDefinitionFilePath, utils.convertMapToReversedObject(this.singleLineBlocksMap));
676-
// Write the into the multi-line-languages.json file.
677-
utils.writeJsonFile(this.multiLineLangDefinitionFilePath, Object.fromEntries(this.multiLineBlocksMap));
676+
// Convert the singleLineBlocksMap to an object.
677+
const singleLineData = utils.convertMapToReversedObject(this.singleLineBlocksMap) as SingleLineLanguageDefinitions;
678+
679+
const multiLineData = Object.fromEntries(this.multiLineBlocksMap) as unknown as MultiLineLanguageDefinitions;
680+
681+
// Write into the single-line-languages.json file.
682+
utils.writeJsonFile<SingleLineLanguageDefinitions>(this.singleLineLangDefinitionFilePath, singleLineData);
683+
684+
// Write into the multi-line-languages.json file.
685+
utils.writeJsonFile<MultiLineLanguageDefinitions>(this.multiLineLangDefinitionFilePath, multiLineData);
678686
}
679687

680688
/**
@@ -701,7 +709,7 @@ export class Configuration {
701709
*/
702710
private setLanguageConfiguration(langId: string, multiLine?: boolean, singleLineStyle?: string): vscode.Disposable {
703711
const internalLangConfig: vscode.LanguageConfiguration = this.getLanguageConfig(langId);
704-
const defaultMultiLineConfig: vscode.LanguageConfiguration = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`);
712+
const defaultMultiLineConfig = utils.readJsonFile(`${__dirname}/../../config/default-multi-line-config.json`) as vscode.LanguageConfiguration;
705713

706714
let langConfig = {...internalLangConfig};
707715

@@ -956,7 +964,7 @@ export class Configuration {
956964
else if (langId == "blade" && this.isLangIdDisabled(langId)) {
957965
vscode.window.showInformationMessage(
958966
`Blade is set as disabled in the "${extensionName}.disabledLanguages" setting. The "${extensionName}.bladeOverrideComments" setting will have no affect.`,
959-
"OK",
967+
"OK"
960968
);
961969

962970
// Set the comments for blade language.
@@ -1011,8 +1019,14 @@ export class Configuration {
10111019
// Log the objects for debugging purposes.
10121020
logger.debug("The language config filepaths found are:", this.languageConfigFilePaths);
10131021
logger.debug("The language configs found are:", this.languageConfigs);
1014-
logger.debug("The supported languages for multi-line blocks:", utils.readJsonFile(this.multiLineLangDefinitionFilePath));
1015-
logger.debug("The supported languages for single-line blocks:", utils.readJsonFile(this.singleLineLangDefinitionFilePath));
1022+
logger.debug(
1023+
"The supported languages for multi-line blocks:",
1024+
utils.readJsonFile<MultiLineLanguageDefinitions>(this.multiLineLangDefinitionFilePath)
1025+
);
1026+
logger.debug(
1027+
"The supported languages for single-line blocks:",
1028+
utils.readJsonFile<SingleLineLanguageDefinitions>(this.singleLineLangDefinitionFilePath)
1029+
);
10161030
}
10171031

10181032
/**

src/extensionData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class ExtensionData {
6161
private getExtensionPackageJsonData(): IPackageJson | null {
6262
// Get the package.json file path.
6363
const packageJSONPath = path.join(this.extensionPath, "package.json");
64-
return readJsonFile(packageJSONPath, false);
64+
return readJsonFile<IPackageJson>(packageJSONPath, false);
6565
}
6666

6767
/**

src/interfaces/utils.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {SingleLineCommentStyle} from "./commentStyles";
2+
3+
/**
4+
* Represents a JSON object.
5+
*/
6+
export interface JsonObject {
7+
[key: string]: JsonValue;
8+
}
9+
10+
/**
11+
* Represents a valid JSON value.
12+
*/
13+
export type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
14+
15+
/**
16+
* Represents a JSON array.
17+
*/
18+
export type JsonArray = JsonValue[] | readonly JsonValue[];
19+
20+
/**
21+
* Structure for single-line language definitions JSON file
22+
*/
23+
export interface SingleLineLanguageDefinitions extends JsonObject {
24+
supportedLanguages: Record<SingleLineCommentStyle, string[]>;
25+
customSupportedLanguages: Record<SingleLineCommentStyle, string[]>;
26+
}
27+
28+
/**
29+
* Structure for multi-line language definitions JSON file
30+
*/
31+
export interface MultiLineLanguageDefinitions extends JsonObject {
32+
supportedLanguages: string[];
33+
customSupportedLanguages: string[];
34+
}

src/utils.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as fs from "node:fs";
22
import * as jsonc from "jsonc-parser";
33
import {logger} from "./logger";
44
import {window} from "vscode";
5+
import {JsonObject, JsonValue} from "./interfaces/utils";
56

67
/**
78
* Read the file and parse the JSON.
@@ -10,10 +11,11 @@ import {window} from "vscode";
1011
* @param {boolean} [throwOnFileMissing=true] Whether to throw an error if the file doesn't exist.
1112
* If `false`, returns `null`. Default is `true`.
1213
*
13-
* @returns {any | null} The JSON file content as an object, or `null` if file doesn't exist and `throwOnFileMissing` is `false`.
14+
* @returns {T | null} The JSON file content as the passed T type (defaulting to a JSON object)
15+
* or `null` if file doesn't exist and `throwOnFileMissing` is `false`.
1416
* @throws Will throw an error if the JSON file cannot be parsed or if file doesn't exist and `throwOnFileMissing` is `true`.
1517
*/
16-
export function readJsonFile(filepath: string, throwOnFileMissing: boolean = true): any | null {
18+
export function readJsonFile<T extends JsonValue = JsonObject>(filepath: string, throwOnFileMissing: boolean = true): T | null {
1719
// Check if file exists first.
1820
// If file doesn't exist...
1921
if (!fs.existsSync(filepath)) {
@@ -46,7 +48,7 @@ export function readJsonFile(filepath: string, throwOnFileMissing: boolean = tru
4648
.showErrorMessage(
4749
`${errorMsg}. The extension cannot continue. Please check the "Auto Comment Blocks" Output Channel for errors.`,
4850
"OK",
49-
"Open Output Channel",
51+
"Open Output Channel"
5052
)
5153
.then((selection) => {
5254
if (selection === "Open Output Channel") {
@@ -57,7 +59,7 @@ export function readJsonFile(filepath: string, throwOnFileMissing: boolean = tru
5759
throw error;
5860
}
5961

60-
return jsonContents;
62+
return jsonContents as T;
6163
}
6264

6365
/**
@@ -94,10 +96,10 @@ function constructJsonParseErrorMsg(filepath: string, fileContent: string, jsonE
9496
* Read the file and parse the JSON.
9597
*
9698
* @param {string} filepath The path of the file.
97-
* @param {any} data The data to write into the file.
99+
* @param {T} data The data to write into the file.
98100
* @returns The file content.
99101
*/
100-
export function writeJsonFile(filepath: string, data: any): any {
102+
export function writeJsonFile<T>(filepath: string, data: T) {
101103
// Write the updated JSON back into the file and add tab indentation
102104
// to make it easier to read.
103105
fs.writeFileSync(filepath, JSON.stringify(data, null, "\t"));
@@ -122,7 +124,7 @@ export function ensureDirExists(dir: string) {
122124
* @param key The key to check in the object
123125
* @returns {RegExp} The reconstructed regex pattern.
124126
*/
125-
export function reconstructRegex(obj: any, key: string) {
127+
export function reconstructRegex(obj: unknown, key: string) {
126128
// If key has a "pattern" key, then it's an object...
127129
if (Object.hasOwn(obj[key], "pattern")) {
128130
return new RegExp(obj[key].pattern);
@@ -139,7 +141,7 @@ export function reconstructRegex(obj: any, key: string) {
139141
* Code based on this StackOverflow answer https://stackoverflow.com/a/45728850/2358222
140142
*
141143
* @param {Map<string, Map<string, string>>} m The Map to convert to an object.
142-
* @returns {object} The converted object.
144+
* @returns {JsonObject} The converted object.
143145
*
144146
* @example
145147
* reverseMapping(
@@ -165,8 +167,8 @@ export function reconstructRegex(obj: any, key: string) {
165167
* }
166168
* }
167169
*/
168-
export function convertMapToReversedObject(m: Map<string, Map<string, string>>): object {
169-
const result: any = {};
170+
export function convertMapToReversedObject(m: Map<string, Map<string, string>>): JsonObject {
171+
const result: JsonObject = {};
170172

171173
// Convert a nested key:value Map from inside another Map into an key:array object,
172174
// while reversing/switching the keys and values. The Map's values are now the keys of

0 commit comments

Comments
 (0)