Skip to content

Commit fbcf7f6

Browse files
committed
fix: ts 7.0 type errors.
Fixed TS 7.0 errors by ensuring: - Variables don't have `undefined` or `null` values before accessing them and providing fallback values if they are undefined with the null coalescing operator (??) and conditional ternary operator (? .. : .. ). - Values from methods are satisfying their expected return types using the `as` keyword and generic typed method calls. - Object keys aren't accessed if the object itself is `undefined` with the optional chaining operator (?.) and proper undefined type guards and conditionals. - `reconstructRegex` util function infers the correct type from the passed `obj` param. - Error stack logging has a fallback of the error message in case the stack is null/undefined. - `convertMapToReversedObject` util function has properly typed `result` instead of implicit `any` type and changed the reverse object mapping to mutate the existing array instead of rebuilding a new one on every iteration.
1 parent 43f97ba commit fbcf7f6

2 files changed

Lines changed: 55 additions & 47 deletions

File tree

src/configuration.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export class Configuration {
9595
// Read the default multi-line config from the JSON file and cache it for later use.
9696
this.defaultMultiLineConfig = utils.readJsonFile(`${configPath}/default-multi-line-config.json`) as vscode.LanguageConfiguration;
9797
// Read the languages to skip from the JSON file and cache it for later use.
98-
this.languagesToSkip = utils.readJsonFile(`${configPath}/skip-languages.jsonc`);
98+
this.languagesToSkip = utils.readJsonFile<JsonObject>(`${configPath}/skip-languages.jsonc`) ?? {};
9999

100100
this.findAllLanguageConfigFilePaths();
101101
this.setLanguageConfigDefinitions();
@@ -255,7 +255,7 @@ export class Configuration {
255255
* ```
256256
*/
257257
public getConfigurationValue<K extends keyof Settings>(key: K): Settings[K] {
258-
return this.getConfiguration().get<Settings[K]>(key);
258+
return this.getConfiguration().get<Settings[K]>(key) as Settings[K];
259259
}
260260

261261
/**
@@ -343,8 +343,8 @@ export class Configuration {
343343
const builtInExtensionsPath = this.extensionData.getExtensionDiscoveryPath("builtInExtensionsPath");
344344

345345
// Read the paths and create arrays of the extensions.
346-
const userExtensions = this.readExtensionsFromDirectory(userExtensionsPath);
347-
const builtInExtensions = this.readExtensionsFromDirectory(builtInExtensionsPath);
346+
const userExtensions = userExtensionsPath ? this.readExtensionsFromDirectory(userExtensionsPath) : [];
347+
const builtInExtensions = builtInExtensionsPath ? this.readExtensionsFromDirectory(builtInExtensionsPath) : [];
348348

349349
// Add all installed extensions (including built-in ones) into the extensions array.
350350
// If running WSL, these will be the WSL-installed extensions.
@@ -372,7 +372,7 @@ export class Configuration {
372372
// If the langId already exists...
373373
if (this.languageConfigFilePaths.has(langId)) {
374374
// Push the new config path into the array of the existing langId.
375-
this.languageConfigFilePaths.get(langId).push(configPath);
375+
this.languageConfigFilePaths.get(langId)?.push(configPath);
376376
}
377377
// Otherwise, if the langId doesn't exist...
378378
else {
@@ -412,7 +412,7 @@ export class Configuration {
412412
// Define a new array as the new AutoClosingPair.
413413
const autoClosingPairsArray: vscode.AutoClosingPair[] = [];
414414
// Loop through the config's autoClosingPairs...
415-
config.autoClosingPairs.forEach((item) => {
415+
(config.autoClosingPairs ?? []).forEach((item) => {
416416
// If the item is an array...
417417
if (Array.isArray(item)) {
418418
// Create a new object with the 1st array element [0] as the
@@ -439,7 +439,7 @@ export class Configuration {
439439
const existingConfig = this.languageConfigs.get(langId);
440440

441441
// Only merge if both configs have comments
442-
if (existingConfig.comments && config.comments) {
442+
if (existingConfig?.comments && config.comments) {
443443
// Start with existing comments as base
444444
const mergedComments = {...existingConfig.comments};
445445

@@ -449,7 +449,9 @@ export class Configuration {
449449
if (Array.isArray(value) && value.length === 0) {
450450
return;
451451
}
452-
mergedComments[key] = value;
452+
if (key === "lineComment" || key === "blockComment") {
453+
mergedComments[key] = value;
454+
}
453455
});
454456

455457
// Update the config with merged comments
@@ -555,7 +557,7 @@ export class Configuration {
555557
this.languageConfigs.forEach((config: vscode.LanguageConfiguration, langId: LanguageId) => {
556558
// If the config object has own property of comments AND the comments key has
557559
// own property of blockComment...
558-
if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "blockComment")) {
560+
if (config.comments && Object.hasOwn(config.comments, "blockComment") && config.comments.blockComment) {
559561
// If the blockComment array includes the multi-line start of "/*"...
560562
if (config.comments.blockComment.includes("/*")) {
561563
// console.log(langId, config.comments);
@@ -627,7 +629,7 @@ export class Configuration {
627629

628630
// If the config object has own property of comments AND the comments key has
629631
// own property of lineComment...
630-
if (Object.hasOwn(config, "comments") && Object.hasOwn(config.comments, "lineComment")) {
632+
if (config.comments && Object.hasOwn(config.comments, "lineComment")) {
631633
let lineComment = config.comments.lineComment;
632634

633635
// Line comments can be a string or an object with a "comment" key.
@@ -752,21 +754,19 @@ export class Configuration {
752754

753755
// Deep-clone the internalLangConfig so modifications never write back
754756
// into the cached `languageConfigs` Map by accident.
755-
let langConfig: vscode.LanguageConfiguration = internalLangConfig
756-
? structuredClone(internalLangConfig)
757-
: {};
757+
let langConfig: vscode.LanguageConfiguration = internalLangConfig ? structuredClone(internalLangConfig) : {};
758758

759759
if (multiLine) {
760760
langConfig.autoClosingPairs = utils.mergeArraysBy<vscode.AutoClosingPair>(
761-
this.defaultMultiLineConfig.autoClosingPairs,
762-
internalLangConfig?.autoClosingPairs,
761+
this.defaultMultiLineConfig.autoClosingPairs ?? [],
762+
internalLangConfig?.autoClosingPairs ?? [],
763763
"open"
764764
);
765765

766766
// Add the multi-line onEnter rules to the langConfig.
767767
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(
768768
Rules.multilineEnterRules,
769-
internalLangConfig?.onEnterRules,
769+
internalLangConfig?.onEnterRules ?? [],
770770
"beforeText"
771771
);
772772

@@ -780,7 +780,7 @@ export class Configuration {
780780
if (this.isLangIdMultiLineCommentOverridden(langId) && langConfig.comments?.blockComment) {
781781
langConfig.comments.blockComment = [
782782
this.getOverriddenMultiLineComment(langId),
783-
langConfig.comments.blockComment[1]
783+
langConfig.comments.blockComment[1],
784784
];
785785
}
786786

@@ -792,6 +792,7 @@ export class Configuration {
792792

793793
// If bladeComments has a value...
794794
if (bladeComments) {
795+
langConfig.comments ??= {};
795796
langConfig.comments.blockComment = bladeComments;
796797
}
797798
}
@@ -805,22 +806,26 @@ export class Configuration {
805806
if (isOnEnter && singleLineStyle) {
806807
// //-style comments
807808
if (singleLineStyle === "//") {
808-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.slashEnterRules, langConfig?.onEnterRules, "beforeText");
809+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.slashEnterRules, langConfig.onEnterRules ?? [], "beforeText");
809810
}
810811
// #-style comments
811812
else if (singleLineStyle === "#") {
812-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.hashEnterRules, langConfig?.onEnterRules, "beforeText");
813+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.hashEnterRules, langConfig.onEnterRules ?? [], "beforeText");
813814
}
814815
// ;-style comments
815816
else if (singleLineStyle === ";") {
816-
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(Rules.semicolonEnterRules, langConfig?.onEnterRules, "beforeText");
817+
langConfig.onEnterRules = utils.mergeArraysBy<vscode.OnEnterRule>(
818+
Rules.semicolonEnterRules,
819+
langConfig.onEnterRules ?? [],
820+
"beforeText"
821+
);
817822
}
818823
}
819824
// If isOnEnter is false AND singleLineStyle isn't false, i.e. a string.
820825
else if (!isOnEnter && singleLineStyle) {
821826
// If langConfig does NOT have a comments key OR
822827
// the comments key exists but does NOT have the lineComment key...
823-
if (!Object.hasOwn(langConfig, "comments") || !Object.hasOwn(langConfig.comments, "lineComment")) {
828+
if (!langConfig.comments || !Object.hasOwn(langConfig.comments, "lineComment")) {
824829
// Add the singleLineStyle to the lineComments key and make sure any
825830
// blockComments aren't overwritten.
826831
langConfig.comments = {...langConfig.comments, lineComment: singleLineStyle};
@@ -838,7 +843,7 @@ export class Configuration {
838843

839844
// Check if isOnEnter OR multiline is true.
840845
if (isOnEnter || multiLine) {
841-
langConfig.onEnterRules.forEach((item) => {
846+
(langConfig.onEnterRules ?? []).forEach((item) => {
842847
// Check if the item has a "beforeText" property and reconstruct its regex pattern.
843848
if (Object.hasOwn(item, "beforeText")) {
844849
item.beforeText = utils.reconstructRegex(item, "beforeText");
@@ -934,19 +939,20 @@ export class Configuration {
934939
* @param {vscode.TextEditor} textEditor The text editor.
935940
* @param {vscode.TextEditorEdit} edit The text editor edits.
936941
*/
937-
private handleSingleLineBlock(textEditor: vscode.TextEditor, edit: vscode.TextEditorEdit) {
942+
private handleSingleLineBlock(textEditor: vscode.TextEditor, edit?: vscode.TextEditorEdit) {
938943
let langId: LanguageId = textEditor.document.languageId;
939944
const singleLineLangs = this.getSingleLineLanguages("supportedLanguages");
940945
const customSingleLineLangs = this.getSingleLineLanguages("customSupportedLanguages");
941946

942947
// Get the langId from the auto-supported langs. If it doesn't exist, try getting it from
943948
// the custom-supported langs instead.
944-
let style: SingleLineCommentStyle | ExtraSingleLineCommentStyles = singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId);
949+
let style: SingleLineCommentStyle | ExtraSingleLineCommentStyles | undefined =
950+
singleLineLangs.get(langId) ?? customSingleLineLangs.get(langId);
945951

946952
if (style && textEditor.selection.isEmpty) {
947953
let line = textEditor.document.lineAt(textEditor.selection.active);
948954
let isCommentLine = true;
949-
let indentRegex: RegExp;
955+
let indentRegex: RegExp | undefined;
950956

951957
if (style === "//" && line.text.search(/^\s*\/\/\s*/) !== -1) {
952958
indentRegex = /\//;
@@ -972,7 +978,7 @@ export class Configuration {
972978
isCommentLine = false;
973979
}
974980

975-
if (!isCommentLine) {
981+
if (!isCommentLine || !indentRegex) {
976982
return;
977983
}
978984

@@ -982,7 +988,7 @@ export class Configuration {
982988
indentedNewLine += style + " ";
983989
}
984990

985-
edit.insert(textEditor.selection.active, indentedNewLine);
991+
edit?.insert(textEditor.selection.active, indentedNewLine);
986992
}
987993
}
988994

src/utils.ts

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export function readJsonFile<T extends JsonValue = JsonObject>(filepath: string,
2323
// If throwOnFileMissing param is true, throw an error.
2424
if (throwOnFileMissing) {
2525
const error = new Error(`JSON file not found: "${filepath}"`);
26-
logger.error(error.stack);
26+
logger.error(error.stack ?? error.message);
2727
throw error;
2828
}
2929
// Otherwise just return null.
@@ -60,7 +60,7 @@ function parseJsonContent<T extends JsonValue = JsonObject>(filepath: string, fi
6060
const errorMsg = "Failed to parse a required JSON file";
6161
const error = new Error(`${errorMsg}: "${filepath}"\n\n\tParse Errors:\n\n${errorMessages}\n\tStack Trace:`);
6262

63-
logger.error(error.stack);
63+
logger.error(error.stack ?? error.message);
6464

6565
window
6666
.showErrorMessage(
@@ -137,18 +137,20 @@ export function ensureDirExists(dir: string) {
137137
* Reconstruct the regex pattern because vscode doesn't like the regex pattern as a string,
138138
* or some patterns are not working as expected.
139139
*
140-
* @param {unknown} obj The object
140+
* @param {T} obj The object
141141
* @param {string} key The key to check in the object
142142
* @returns {RegExp} The reconstructed regex pattern.
143143
*/
144-
export function reconstructRegex(obj: unknown, key: string): RegExp {
144+
export function reconstructRegex<T extends object, K extends keyof T>(obj: T, key: K): RegExp {
145+
const value = obj[key];
146+
145147
// If key has a "pattern" key, then it's an object...
146-
if (Object.hasOwn(obj[key], "pattern")) {
147-
return new RegExp(obj[key].pattern);
148+
if (typeof value === "object" && value !== null && Object.hasOwn(value, "pattern")) {
149+
return new RegExp((value as unknown as {pattern: string}).pattern);
148150
}
149151
// Otherwise it's a string.
150152
else {
151-
return new RegExp(obj[key]);
153+
return new RegExp(value as string);
152154
}
153155
}
154156

@@ -185,7 +187,7 @@ export function reconstructRegex(obj: unknown, key: string): RegExp {
185187
* }
186188
*/
187189
export function convertMapToReversedObject<T extends JsonValue = JsonObject>(m: Map<string, Map<string, string>>): T {
188-
const result = {};
190+
const result: Record<string, Record<string, string[]>> = {};
189191

190192
// Convert a nested key:value Map from inside another Map into an key:array object,
191193
// while reversing/switching the keys and values. The Map's values are now the keys of
@@ -199,15 +201,15 @@ export function convertMapToReversedObject<T extends JsonValue = JsonObject>(m:
199201

200202
// Reverse the inner object mapping.
201203
//
202-
// Loop through the object (o) keys, assigns a new object (r) with the value of the
203-
// object key (k) as the new key (eg. "//") and the new value is an array of all
204-
// the original object keys (o[k]) (eg. "php").
205-
// If the key (o[k]) already exists in the new object (r), then just add the
206-
// original key to the array, otherwise start a new array ([]) with the original
207-
// key as value ( (r[o[k]] || []).concat(k) ).
208-
// Add this new reversed object to the result object with the outer map key
209-
// as the key.
210-
result[key] = Object.keys(o).reduce((r, k) => Object.assign(r, {[o[k]]: (r[o[k]] || []).concat(k)}), {});
204+
// Loop through the object (o) keys, and for each one, push the key (itemKey, eg. "php")
205+
// onto the array keyed by its value (o[itemKey], eg. "//") in the reversed object,
206+
// creating that array on first use. Add this reversed object to the result object
207+
// with the outer map key as the key.
208+
result[key] = Object.keys(o).reduce<Record<string, string[]>>((reversed, itemKey) => {
209+
const value = o[itemKey];
210+
(reversed[value] ??= []).push(itemKey);
211+
return reversed;
212+
}, {});
211213
}
212214
return result as T;
213215
}
@@ -279,9 +281,9 @@ function validateDevEnvVariables() {
279281
// Trim whitespace and resolve the path to an absolute path
280282
let devPath = path.resolve(process.env.DEV_USER_EXTENSIONS_PATH.trim());
281283

282-
let stats: fs.Stats;
284+
let stats: fs.Stats | undefined;
283285
let errorMsg: string = "";
284-
let errorData: Error;
286+
let errorData: Error | undefined;
285287

286288
// Get the file system stats for the path to check if it exists.
287289
// statSync throws an exception if the no file system data exists for the path,
@@ -293,7 +295,7 @@ function validateDevEnvVariables() {
293295
const errorCode = nodeError.code || "UNKNOWN";
294296

295297
// Handle specific file system errors with user-friendly messages.
296-
const errorMessages = {
298+
const errorMessages: Record<string, string> = {
297299
ENOENT: "Path from env variable 'DEV_USER_EXTENSIONS_PATH' does not exist",
298300
EACCES: "Permission denied accessing path from env variable 'DEV_USER_EXTENSIONS_PATH'",
299301
UNKNOWN: "Unknown error accessing the path from env variable 'DEV_USER_EXTENSIONS_PATH'",

0 commit comments

Comments
 (0)