Skip to main content

DOCUMENTS-API-Typen nutzen

Die wichtigsten Objekte sind ohne eigenes Zutun typisiert: context, util und alle API-Klassen wie DocFile, FileResultset, SystemUser oder Register kommen aus portalScripting.d.ts. Den größten Nutzen bringen aber unsere eigenen Mappentypen aus fileTypes.d.ts.

Mappentypen: Felder werden geprüft

Für jeden Mappentyp erzeugt die Extension einen eigenen Typ (z.B. dexRecord, dexRecordCategory, RIBProject). Er kennt alle Felder, Register und Referenzfelder. Die API-Methoden sind so typisiert, dass VS Code den richtigen Mappentyp selbst erkennt:

const rec = context.createFile("dexRecord");   // Typ: dexRecord
rec.Subject = "Rechnung 4711";                  // OK, Feld existiert
rec.Subjekt = "Rechnung 4711";                  // Fehler: Property 'Subjekt' does not exist on type 'dexRecord'
rec.getRegisterByName("Attachments");           // Registername wird geprüft

for (const cat of new FileResultset("dexRecordCategory", "", "")) {
    cat.CategoryName;                           // Typ: dexRecordCategory
}

Ein zusätzliches /** @type {dexRecord} */ vor context.createFile("dexRecord") ist deshalb unnötig.

context.file eingrenzen

context.file ist nur als allgemeines DocFile bekannt, denn das Skript kann an jeder Mappe hängen. Wer weiß, welcher Mappentyp ankommt, grenzt ihn per Cast ein. Aus Gadget_DexDocumentTreeNavigator.js:

switch(fileTypeForFilter) {
    case "dexRecordConfiguration":
        const dexRecConf = /** @type {DocFile & dexRecordConfiguration} */ (file);
        filterValue = dexRecConf?.UniqueID || "";
        break;
    case "dexRecordCategory":
        const dexRecCat = /** @type {DocFile & dexRecordCategory} */ (file);

Referenzfelder liefern ebenfalls typisierte Mappen. Weil eine Referenz leer sein kann, gehört null mit in den Typ (DexRecordReference.mjs):

/** @type {null | DocFile & dexRecordConfiguration} */ (this.dossierFile.getReferenceFile("DexRecordConfiguration"))

Auch Feldnamen in Variablen lassen sich einschränken: /** @type {keyof dexRecordFields} */ (key).

Gadgets und Client-Code

Die Gadget-API bringt eigene Typen mit (gadgetApi.d.ts, documentsClientSdk.d.ts). Aus Gadget_DEXPRO_ShowDBData.js:

/**
 * @param {GadgetContext} gadgetContext
 * @param {otris} otris
 * @returns {otris.gadget.gui.Form}
 */
function showDBData(gadgetContext, otris) {
    ...
    /** @type {ContainerButtonConfig} */
    let showDataButtonConfig = {
        "id": "showDataButtonId",
        ...
    }

Variablen, die erst im Browser existieren, deklariert man nur für die Typprüfung (Gadget_DEXPRO_MailQueue.js):

/**
 * Nur zur Typpruefung: In den clientseitigen Funktionen (clientScript,
 * addClientFunction, addOnGadgetLoad) stellt der Client diese Variable bereit.
 * Serverseitig wird sie nie gelesen.
 *
 * @type {documents.sdk.DocumentsContext}
 */
var documentsContext;

Skript-Parameter

Parameter eines Portalskripts stellt der Server als globale Variablen bereit. Die Typings kennen sie nicht. Das saubere Muster: zur Laufzeit prüfen, casten und den Grund dokumentieren (Gadget_DexRecordCategory_FiletypeFilter.js):

/**
 * `query` is injected as a global variable by the script engine and is not part of the typings.
 */
// @ts-ignore
const autoCompleteQuery = /** @type {string} */ (typeof query === "string" ? query : "");