From 1f1e94340d4a3f7ebde02db9f9e322a10f17f822 Mon Sep 17 00:00:00 2001 From: Max Berger Date: Sun, 12 Jul 2026 09:31:54 +0200 Subject: [PATCH] Fix new JS verification errors and force specific TSC version The new TSC compiler does stricter checking on the JS documentation strings. All documentation strings have been updated to pass with the current version (7.0.2) In addition, the version of the TSC compiler used during the github action will be fixed to 7.0.2, so that we don't get these type of sudden errors again in the future. Unfortunately this means we need to periodically update this manually. --- .github/workflows/test.yml | 2 +- radicale/web/internal_data/js/api/api.js | 14 ++++---- radicale/web/internal_data/js/api/sharing.js | 32 +++++++++---------- .../js/scenes/CollectionsScene.js | 2 +- .../js/scenes/ShareCollectionScene.js | 6 ++-- .../js/utils/collections_cache.js | 14 ++++---- .../internal_data/js/utils/form_validator.js | 18 +++++------ radicale/web/internal_data/js/utils/misc.js | 2 +- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4e4934f..1eecd5ac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -293,7 +293,7 @@ jobs: node-version: 24 - name: JS Type Check run: | - npx -p typescript tsc -p radicale/web/jsconfig.json + npx -y -p typescript@7.0.2 tsc -p radicale/web/jsconfig.json lint: name: Lint diff --git a/radicale/web/internal_data/js/api/api.js b/radicale/web/internal_data/js/api/api.js index 30ee46bb..41cfe8b8 100644 --- a/radicale/web/internal_data/js/api/api.js +++ b/radicale/web/internal_data/js/api/api.js @@ -28,7 +28,7 @@ import { create_request, to_error_message } from "./common.js"; * Find the principal collection. * @param {?string} user * @param {?string} password - * @param {function(?Collection, ?string):void} callback Returns result or error + * @param {(collection: Collection | null, error: string | null) => void} callback Returns result or error * @return {XMLHttpRequest} */ export function get_principal(user, password, callback) { @@ -81,7 +81,7 @@ export function get_principal(user, password, callback) { * @param {string} user * @param {?string} password * @param {Collection} collection - * @param {function(?Array, ?string):void} callback Returns result or error + * @param {(collections: Array | null, error: string | null) => void} callback Returns result or error * @return {XMLHttpRequest} */ export function get_collections(user, password, collection, callback) { @@ -267,7 +267,7 @@ function _parse_collection(response, collection_href) { * @param {?string} password * @param {string} collection_href Must always start and end with /. * @param {File} file - * @param {function(?string):void} callback Returns error or null + * @param {(error: string | null) => void} callback Returns error or null * @return {XMLHttpRequest} */ export function upload_collection(user, password, collection_href, file, callback) { @@ -291,7 +291,7 @@ export function upload_collection(user, password, collection_href, file, callbac * @param {string} user * @param {?string} password * @param {Collection} collection - * @param {function(?string):void} callback Returns error or null + * @param {(error: string | null) => void} callback Returns error or null * @return {XMLHttpRequest} */ export function delete_collection(user, password, collection, callback) { @@ -315,7 +315,7 @@ export function delete_collection(user, password, collection, callback) { * @param {?string} password * @param {Collection} collection * @param {boolean} create - * @param {function(?string):void} callback Returns error or null + * @param {(error: string | null) => void} callback Returns error or null * @return {XMLHttpRequest} */ function create_edit_collection(user, password, collection, create, callback) { @@ -394,7 +394,7 @@ function create_edit_collection(user, password, collection, create, callback) { * @param {string} user * @param {?string} password * @param {Collection} collection - * @param {function(?string):void} callback Returns error or null + * @param {(error: string | null) => void} callback Returns error or null * @return {XMLHttpRequest} */ export function create_collection(user, password, collection, callback) { @@ -405,7 +405,7 @@ export function create_collection(user, password, collection, callback) { * @param {string} user * @param {?string} password * @param {Collection} collection - * @param {function(?string):void} callback Returns error or null + * @param {(error: string | null) => void} callback Returns error or null * @return {XMLHttpRequest} */ export function edit_collection(user, password, collection, callback) { diff --git a/radicale/web/internal_data/js/api/sharing.js b/radicale/web/internal_data/js/api/sharing.js index e83f440b..f5841348 100644 --- a/radicale/web/internal_data/js/api/sharing.js +++ b/radicale/web/internal_data/js/api/sharing.js @@ -41,9 +41,9 @@ import { create_request, to_error_message } from "./common.js"; * @param {?string} password * @param {string} path * @param {object} body - * @param {function(string):void} on_success - * @param {?function():void} on_not_found - * @param {?function(string):void} on_error + * @param {(response: string) => void} on_success + * @param {(() => void) | null} on_not_found + * @param {((error: string) => void) | null} on_error * @returns {XMLHttpRequest} */ function call_sharing_api( @@ -92,7 +92,7 @@ function call_sharing_api( /** * @param {string} user * @param {?string} password - * @param {function(import("../api/sharing.js").ServerFeatures, ?string):void} callback + * @param {(features: import("../api/sharing.js").ServerFeatures, error: string | null) => void} callback */ export function discover_server_features(user, password, callback) { call_sharing_api( @@ -163,12 +163,12 @@ export const BDAY_CONFIG = Object.freeze([ ]); export class ShareConfig { + /** @type {Record} */ + _values = {}; /** * @param {ShareConfig|Record} [data] */ constructor(data = {}) { - /** @type {Record} */ - this._values = {}; let rawData = data; if (data instanceof ShareConfig) { rawData = data._values; @@ -310,8 +310,8 @@ export class Share { /** @type {number} */ this.TimestampUpdated = data.TimestampUpdated || 0; /** @type {Object} */ this.Properties = data.Properties || {}; /** @type {string} */ this.Conversion = data.Conversion || ""; - /** @type {ShareActions} */ this._Actions = new ShareActions(); - this.Actions = data.Actions || {}; + /** @type {ShareActions} */ + this._Actions = data.Actions instanceof ShareActions ? data.Actions : new ShareActions(data.Actions || {}); } /** @@ -351,7 +351,7 @@ export class Share { * @param {string} user * @param {?string} password * @param {?import("../models/collection.js").Collection} collection - * @param {function(Array, ?string):void} callback + * @param {(shares: Array, error: string | null) => void} callback */ export function reload_sharing_list(user, password, collection, callback) { let body = collection ? { PathMapped: decodeURIComponent(collection.href) } : {}; @@ -412,7 +412,7 @@ export function get_property_key(type, property) { * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function add_share_by_token( user, @@ -452,7 +452,7 @@ export function add_share_by_token( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function add_share_by_map( user, @@ -494,7 +494,7 @@ export function add_share_by_map( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function delete_share_by_token( user, @@ -526,7 +526,7 @@ export function delete_share_by_token( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function delete_share_by_map( user, @@ -557,7 +557,7 @@ export function delete_share_by_map( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function update_share_by_token( user, @@ -597,7 +597,7 @@ export function update_share_by_token( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function update_share_by_map( user, @@ -641,7 +641,7 @@ export function update_share_by_map( * @param {string} user * @param {?string} password * @param {Share} share - * @param {function(?string):void} callback + * @param {(error: string | null) => void} callback */ export function update_incoming_share( user, diff --git a/radicale/web/internal_data/js/scenes/CollectionsScene.js b/radicale/web/internal_data/js/scenes/CollectionsScene.js index eee39a47..59a5cbf9 100644 --- a/radicale/web/internal_data/js/scenes/CollectionsScene.js +++ b/radicale/web/internal_data/js/scenes/CollectionsScene.js @@ -42,7 +42,7 @@ export class CollectionsScene { * @param {string} user * @param {?string} password * @param {Collection} principal_collection The princial collection - * @param {function(?string):void} onerror Called when an error occurs, before the + * @param {(error: string | null) => void} onerror Called when an error occurs, before the * scene is popped. */ constructor(user, password, principal_collection, onerror) { diff --git a/radicale/web/internal_data/js/scenes/ShareCollectionScene.js b/radicale/web/internal_data/js/scenes/ShareCollectionScene.js index 09683cf4..ddffc894 100644 --- a/radicale/web/internal_data/js/scenes/ShareCollectionScene.js +++ b/radicale/web/internal_data/js/scenes/ShareCollectionScene.js @@ -169,10 +169,10 @@ function update_share_list(user, password, collection, errorHandler) { * @param {import('../api/sharing.js').Share} share * @param {HTMLElement} template * @param {string} delete_label - * @param {function(string, string, import('../api/sharing.js').Share, function(?string):void):void} delete_action - * @param {function():void} [onDeleteSuccess] Optional extra callback after a successful delete. + * @param {(user: string, password: string | null, share: import('../api/sharing.js').Share, callback: (error: string | null) => void) => void} delete_action + * @param {(() => void) | null} [onDeleteSuccess] Optional extra callback after a successful delete. */ -function add_share_row_node(user, password, collection, share, template, delete_label, delete_action, onDeleteSuccess) { +function add_share_row_node(user, password, collection, share, template, delete_label, delete_action, onDeleteSuccess = null) { let pathortoken = share["PathOrToken"] || ""; let node = /** @type {HTMLElement} */ (template.cloneNode(true)); node.classList.remove("hidden"); diff --git a/radicale/web/internal_data/js/utils/collections_cache.js b/radicale/web/internal_data/js/utils/collections_cache.js index d0b6e7cc..fa65ab0b 100644 --- a/radicale/web/internal_data/js/utils/collections_cache.js +++ b/radicale/web/internal_data/js/utils/collections_cache.js @@ -31,7 +31,7 @@ class CollectionsCache { this.server_features = null; /** @type {?XMLHttpRequest} */ this.collections_req = null; /** @type {?XMLHttpRequest} */ this.shares_req = null; - /** @type {?Array<{ onerror: function(string):void, displayData: function(Array):void }>} */ + /** @type {Array<{ onerror: (error: string) => void, displayData: (shares: Array) => void }> | null} */ this._sharing_callbacks = null; } @@ -54,8 +54,8 @@ class CollectionsCache { * @param {string} user * @param {?string} password * @param {import("../models/collection.js").Collection} principal_collection - * @param {function(string):void} onerror - * @param {function(Array, Array, boolean):void} displayData + * @param {(error: string) => void} onerror + * @param {(collections: Array, shares: Array, loaded: boolean) => void} displayData */ getChildCollections(user, password, principal_collection, onerror, displayData) { if (this.child_collections !== null && this.incoming_shares !== null) { @@ -105,8 +105,8 @@ class CollectionsCache { /** * @param {string} user * @param {?string} password - * @param {function(string):void} onerror - * @param {function(Array):void} displayData + * @param {(error: string) => void} onerror + * @param {(shares: Array) => void} displayData */ getSharingList(user, password, onerror, displayData) { if (this.incoming_shares !== null) { @@ -140,8 +140,8 @@ class CollectionsCache { /** * @param {string} user * @param {?string} password - * @param {function(string):void} onerror - * @param {function(import("../api/sharing.js").ServerFeatures):void} displayData + * @param {(error: string) => void} onerror + * @param {(features: import("../api/sharing.js").ServerFeatures) => void} displayData */ getServerFeatures(user, password, onerror, displayData) { if (this.server_features !== null) { diff --git a/radicale/web/internal_data/js/utils/form_validator.js b/radicale/web/internal_data/js/utils/form_validator.js index 6a5d3a94..989215f4 100644 --- a/radicale/web/internal_data/js/utils/form_validator.js +++ b/radicale/web/internal_data/js/utils/form_validator.js @@ -30,13 +30,13 @@ export class FormValidator { */ constructor(error_handler) { this.error_handler = error_handler; - /** @type {Array<{field: HTMLInputElement, validation_method: function(): ?string}>} */ + /** @type {Array<{field: HTMLInputElement, validation_method: () => string | null}>} */ this.validation_methods = []; } /** * @param {HTMLInputElement} field - * @param {function(): ?string} validation_method + * @param {() => string | null} validation_method */ addValidator(field, validation_method) { this.validation_methods.push({ field, validation_method }); @@ -67,7 +67,7 @@ export class FormValidator { * Validates that the input is not empty. * @param {HTMLInputElement} input * @param {string} field_name - * @returns{function(): ?string} + * @returns {() => string | null} */ export function validate_non_empty(input, field_name) { return () => { @@ -83,7 +83,7 @@ export function validate_non_empty(input, field_name) { * @param {HTMLInputElement} input * @param {string} target * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_not_empty_or_equals(input, target, field_name) { return () => { @@ -102,7 +102,7 @@ export function validate_not_empty_or_equals(input, target, field_name) { * Validates that the input is a valid HREF. * @param {HTMLInputElement} input * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_href(input, field_name) { return () => { @@ -124,7 +124,7 @@ export function validate_href(input, field_name) { * Validates that the input is a valid color. * @param {HTMLInputElement} input * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_color(input, field_name) { return () => { @@ -144,7 +144,7 @@ export function validate_color(input, field_name) { * @param {HTMLInputElement} input * @param {string} target * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_equals(input, target, field_name) { return () => { @@ -160,7 +160,7 @@ export function validate_equals(input, target, field_name) { * Validates that at least one file is selected in a file input. * @param {HTMLInputElement} input * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_files(input, field_name) { return () => { @@ -175,7 +175,7 @@ export function validate_files(input, field_name) { * Validates that the input is a valid integer (if not empty). * @param {HTMLInputElement} input * @param {string} field_name - * @returns {function(): ?string} + * @returns {() => string | null} */ export function validate_integer(input, field_name) { return () => { diff --git a/radicale/web/internal_data/js/utils/misc.js b/radicale/web/internal_data/js/utils/misc.js index 4444b892..ed39c5f0 100644 --- a/radicale/web/internal_data/js/utils/misc.js +++ b/radicale/web/internal_data/js/utils/misc.js @@ -156,7 +156,7 @@ export function get_element(node, selector) { * * @param {string} str * @param {number} max - * @returns {str} + * @returns {string} */ export function trim_to_max(str, max) { if (str.length > max - 2) {