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 : "");
No comments to display
No comments to display