Skip to main content

Eigene Typen definieren

Sobald ein Objekt an mehreren Stellen vorkommt (Konfigurationen, Rückgabewerte, JSON von externen Schnittstellen), bekommt es einen eigenen Typ mit @typedef. Der Name ist danach in der ganzen Datei nutzbar.

Objekt-Strukturen: @typedef und @property

Aus Gadget_DexRecordCategory_FiletypeFilter.js. Jede Eigenschaft hat Typ, Name und eine kurze Erklärung:

/**
 * One selectable filter property of the grid.
 * @typedef {object} FilterProperty
 * @property {string} propertyKey technical key stored in `FileTypeFilterData`
 * @property {string} label       label shown in the key column
 * @property {string} editorType  property grid editor, `SELECT` or `TEXT`
 * @property {string} searchText  lower cased text the autocomplete matches against
 */

Das vollständigste Beispiel im Code steht in DEXPRO__ExtDataProfiles.js. Es zeigt optionale Eigenschaften, Funktionen als Eigenschaft, Dictionaries und verschachtelte Typen:

/**
 * @typedef {Object} ExtDataProfile
 * @property {string} id unique id, "<source>.<entity>" (matched case-insensitively)
 * @property {string} [title] multilingual heading of the gadget
 * @property {(data: Object, record: any) => boolean} [match] auto-detection, called with the first data object
 * @property {string[]} [cardTitle] JSON keys, the first non-empty value is the card heading
 * @property {Object<string, string>} [labels] key / base name / path -> multilingual label
 * @property {ExtDataGroup[]} [groups] sections of the card, unlisted rows go to "Weitere Angaben"
 * @property {Array<string|RegExp>} [include] allowlist for top-level rows (empty = everything)
 */

Bestehende Typen erweitern

Mit & wird ein Typ um Felder ergänzt, ohne ihn zu kopieren. Aus Gadget_DEXPRO_Contract_Certs_MailNotifications.js:

/**
 * A MailConfig while being edited in the form, carrying the transient
 * "Auswählen" checkbox state (stripped before persisting).
 *
 * @typedef {MailConfig & { __selected?: boolean }} MailConfigDraft
 */

Konstanten als Enum: @enum und @readonly

Aus DexDossier_OnFileView.js. @readonly verhindert versehentliches Überschreiben, @enum {string} macht den Objektnamen als Typ nutzbar (@param {NOTIFICATION_TYPE} type):

/**
 * Notification types accepted by {@link notifyUser}.
 * @readonly
 * @enum {string}
 */
const NOTIFICATION_TYPE = {
    INFO: "info",
    WARNING: "warning",
    ERROR: "error",
    SUCCESS: "success",
    DEBUG: "debug"
};

Typen über Dateigrenzen teilen

Ein Typ wird einmal in der Bibliothek definiert und überall per import() wiederverwendet. MailInfo steht in DEXPRO__MailQueueLib.js und wird in Gadget_DEXPRO_MailQueue.js so eingebunden:

/** @typedef {import("../Dexpro_Lib.cat/DEXPRO__MailQueueLib").MailInfo} MailInfo */

Relative Pfade (./ oder ../) funktionieren immer. Ein Skriptname ohne Pfad (z.B. import("DexRecordReference")) funktioniert nur, wenn er unter paths in der jsconfig.json steht. Sonst meldet VS Code "Cannot find module".

Für Fortgeschrittene

  • Typ aus einer Funktion ableiten: @property {ReturnType<typeof findExtDataProfile>} profile (Gadget_DEXPRO_showExtData.js). Der Typ ändert sich automatisch mit, wenn sich die Funktion ändert.
  • Generics mit @template: In DEXPRO_Gadget_FormLib.js liefert getFormElements ein Objekt mit genau den Schlüsseln des übergebenen Schemas:
/**
 * Gets fields of the form based on the provided schema.
 * @this {otris.gadget.gui.Form}
 * @template {FieldSchema} T
 * @param {T} fieldSchema
 * @returns {Record<keyof T, otris.gadget.gui.Element>}
 */
_FormPrototype.getFormElements = function(fieldSchema) {