NEWS
Typprüfung mit javascript.d.ts
-
Was tut die?
-
-
Eine Frage an die Typprüfung Profis…
Es hat bis jetzt alles wunderbar funktioniert und die Vorteile einer aktivierten Typprüfung sind wirklich greifbar.
Bei einem Skript habe ich nun allerdings ein Verhalten, welches ich mir nicht erklären kann.
Einmal wurde die Benutzung einer Variable nicht angemeckert, die noch nicht definiert war.
Im zweiten Fall ist es genau umgekehrt. Eine deklarierte Variable wird als nicht vorhanden angezeigt.
Hat jemand eine Idee, warum das so sein könnte.
1. Beispiel: fehlende Definition erzeugt keine Fehleranzeige:
// var logFarbig = true; // zum Test auskommentiert
Die Variable ist in anderen Skripten (in keine globalen, kein Skript mit Typdatei, ohne Zusammenhang) vorhanden und wird nicht angemeckert:
.
.
Der 2. Fall. Variable vorhanden, wird aber als fehlend angemeckert:
`var triggerArr = []; //... triggerArr.push("javascript."+instance+"."+pfad+listen[i]+".event"); //.. on({id:triggerArr,change:'any'}, function (obj) {` filename="triggerArr.png" index="0">~~ Als Typdatei habe ich die **~~[b]~~javascript.d.ts[/b]** von gestern aktiv, in der Arrays für Subscriptions enthalten sein müsste.[/i]
-
Einmal wurde die Benutzung einer Variable nicht angemeckert, die noch nicht definiert war.
Hat jemand eine Idee, warum das so sein könnte.
1. Beispiel: fehlende Definition erzeugt keine Fehleranzeige:
// var logFarbig = true; // zum Test auskommentiert ```` `
Bei der Deklaration mit var kann das vorkommen, das die Deklaration script übergreifender ist. Ist mir auch schon auf die Füße gefallen.
Nimm lieber let, das hat nur den lokalen scope.
Ich versuche mittlerweile alles nur noch mit let zu deklarieren (außer ich will bewusst den Scope vergrößern) und habe keine Probleme mehr.
let logFarbig = true;
Mit let kannst du die Gültigkeit einer Variablen auch nur auf einen Block eingrenzen. Also beispielsweise nur für eine for Schleife.
Bei deinem 2. Fall sehe ich erstmal kein Problem.
-
Bei der Deklaration mit var kann das vorkommen, das die Deklaration script übergreifender ist. Ist mir auch schon auf die Füße gefallen.
Nimm lieber let, das hat nur den lokalen scope.
Ich versuche mittlerweile alles nur noch mit let zu deklarieren (außer ich will bewusst den Scope vergrößern) und habe keine Probleme mehr.
let logFarbig = true; ```` `
Danke für den Tipp [emoji106], den ich dann ab jetzt beherzigen werde.
Komisch ist das schon. Kann doch nicht im Sinne des Erfinders sein [emoji15]
Gesendet von iPad mit Tapatalk
-
Das ist ja auch https://www.planningforaliens.com/blog/2016/04/11/why-js-development-is-crazy/
Hier ist eine Erklärung :
-
Das ist ja auch https://www.planningforaliens.com/blog/2016/04/11/why-js-development-is-crazy/
Hier ist eine Erklärung :
ja, javascript :?
Ich habe mir derweil auch eine Erklärung dazu angesehen https://developer.mozilla.org/de/docs/W … ements/let.
1:1 kann ich das dann nicht übernehmen. Umdenken ist angesagt.
Das andere Problem lag bei mir :roll:
Hatte "übersehen", dass ich das Array versehentlich innerhalb einer Funktion deklariert hatte.
Dann kann ich sie natürlich nicht an anderer Stelle im Skript verwenden. :lol:
Die Typeprüfung hat da richtig reagiert.
War wohl wg. dem 1. Punkt etwas sensibel. Der mich immer noch verwundert
-
Bei der Deklaration mit var kann das vorkommen, das die Deklaration script übergreifender ist. Ist mir auch schon auf die Füße gefallen.
Nimm lieber let, das hat nur den lokalen scope.
Ich versuche mittlerweile alles nur noch mit let zu deklarieren (außer ich will bewusst den Scope vergrößern) und habe keine Probleme mehr.
let logFarbig = true;
Mit let kannst du die Gültigkeit einer Variablen auch nur auf einen Block eingrenzen. Also beispielsweise nur für eine for Schleife. `
Unabhängig von der Möglichkeit let zu verwenden…
Ich bin der Meinung, dass sich das Verhalten von vscode vorher nicht so war.
Hat jemand eine Idee, wie ich verhindern kann, das vscode Variablen, die in unterschiedlichen Skripten deklariert werden anmeckert.
Die Skripte sind für sich eigenständig und haben nichts miteinander zu tun.
Ich verwende:
/// <reference path="../javascript.d.ts"> /// <reference path="../javascript-global.d.ts"> // @ts-check</reference></reference>
javascript.d.ts" für den Javascript Adapter
javascript-global.d.ts für meine eigenen globalen Funktionen
Gefühlt zeitgleich mit dem Problem werden nun meine globalen Funktionen bei der Typehilfe nicht mehr korrekt angezeigt.
Die Variablennamen werden angezeigt, aber nicht mehr die Beschreibung und die richtigen Typen (immer any, als z.B. string).
Ich vermute, es ist etwas in der javascript-global.d.ts. Sehe aber nicht, was es sein könnte.
! ````
import child_process = require("child_process");
! // tslint:disable:no-namespace
declare global {namespace iobGlobal {
! type loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error";
}
! // Logikwerte:
// ===========================================
/**
* true/false als Ergebnis ob der aktuelle Tag ein Arbeitstag ist
/
function arbeitstag(): boolean;
! /*
* true/false als Ergebnis ob der aktuelle Tag ein Urlaubstag ist
/
function urlaubstag(): boolean;
! /*
* true wenn die aktuelle Zeit im angegebenen Zeitraum liegt
* @param strLower Startzeit formatiert als hh:mm:ss
* @param strUpper Endzzeit formatiert als hh:mm:ss
/
function isTimeInRange(strLower:string, strUpper:string): boolean;
! // Werte ausgeben
// ===========================================
/*
* gibt den Wochentag (Montag, Dienstag, ...) als Text zurück
/
function wochentag(): string;
! /*
* Gibt das Delta zwischen einem im Datenpunkt gespeicherten
* Epoch Wert und der aktuellen Zeit formatiert zurück.
*
* Zum speichern einer Zeit in einen Datenpunkt kann die Funktion
* dateEpochNow() verwendet werden.
/
function deltazeit(datenpunkt:string): string;
! /*
* gibt das aktuelle Datum & die Zeit formatiert aus
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param format Format der Ausgabe, Format wie bei formateDate()
* @param msBool true: ms im Format .ms werden angehängt
/
function dateNow(format?:string,msBool?:boolean): string;
! /*
* Gibt die Anzahl der gefundenen Subscriptions zurück.
* Listet alle Subscriptions innerhalb der Javascript-Instanz auf, die dem Filter entsprechen, im Log auf.
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param scriptname (optional) "alle", "Name des Skripts", "FEQ", usw.
* @param level (optional) loglevel für logs, default: "info"
* // Filter (scriptname):
* --------------------
* sucht den String in den überwachten Datenpunkten der Javascript-Instanz
* und sucht den String in den Scriptnamen innerhalb der Javascript-Instanz
* keine Angabe -> Filter = "alle" (alle Subscriptions der Instanz)
*
* loglevel
* --------
* loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
*
* Beispiele:
* ----------
* prinSubs(); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* prinSubs("alle"); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* printSubs("Skriptname"); alle Subscriptions innerhalb des aufrufenden Scripts
* printSubs("FEQ"); alle Subscriptions, die im Script oder Objekt "FEQ" enthalten
/
function printSubs(scriptname?:string,level?:iobGlobal.loglevel): number;
! /*
* Gibt eine zufällige Zahl zwischen min und max zurück
* @param min kleinster möglicher Wert der Zufahlszahl
* @param max größter möglicher Wert der Zufahlszahl
/
function rand(min:number,max:number) : number;
! // Funktionen
// ===========================================
! /*
* Rundet eine Zahl auf die angegebenen Stellen
* @param wert Zahl, die gerundet werden soll
* @param stellen Anzahl der Stellen
/
function runden(wert: number, stellen: number): number;
! /*
* Ansage über den sayit Adapter
* @param text Ansage, die abgespielt werden soll
* @param vol (optional) Lautstärke der Ansage von 1-100
* @param instanz? (optional) ohne Angabe: default Instanz. Mit Angabe: sayit Instanz
*/
function sayit(text: string, vol?: number, instanz?: number): void;/** * formatiert eine Übergabe in Sekunden zu einem lesbaren String * @param sekunden Sekunden, die lesbar ausgegeben werden sollen */ function sek2txt(sekunden:number | string): string;
! /**
* Loglevel innerhalb eines Javascripts
* das Script muss am die Variable des gewünschten Loglevels definiert haben, z.B.:
* var loglevel = "info";
* @param logtext Text, der in der Logausgabe erscheinen soll
* @param level "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
* @param color? (optional) ohne Angabe: vordefinierte Farbe für den level, mit Angabe: gültige html Farbe
* Rückgabe: verwendeter Loglevel zur Information
/
function logs(logtext:string,level:iobGlobal.loglevel,color?:string):string;
! /*
* Schreibt einen Text mit Zeitstempel in eine Logdatei
* Datei: /opt/iobroker/iobroker-data/scripts-logf.log
* var loglevel = "info";
* @param text Text, der mit einem Zeitstempel in die Logdatei geschrieben werden soll
/
function logf(text:string);
! function event(text: string, list?: string) : void;
! // Helfer:
// ===========================================
/*
* liefere Anzahl n nbsp in utf-8, wenn str nicht angegeben oder n-mal str
* @param n Anzahl von nbsp;, bzw. wenn str angegeben: ANzahl des Strings in str
* @param str (optional) String, welche n mal erzeugt wird
/
function fill(n:number,str?:string): string;
! /*
* gibt das aktuelle Datum in Epoch zurück
/
function dateEpochNow() : number;
! /*
* entfernt aus einem html String die html Tags
*/
function html2str(html:string) : number;
! // Workaround, Fehler in der ioBroker Typprüfung
// ===========================================
function getSubscriptions(): any;
! const name: string;
! }
! ````[EDIT]
Das Thema mit den anmecker der "doppelten" Variablen lässt sich soweit eingrenzen, dass immer das Skript zählt, welches sich in einer höheren Verzeichnisebene befindet.
Beispiel:
1. Ebene: var id = [""]
2. Ebene: var id = "";
Dann meckert die Typprüfung, das in der Datei in der 2. Ebene ein anderer Typ verwendet wird (string statt string[]).
Die Skripte haben so nichts miteinander zu tun.
kann man das eventuell mit einer Einstellung in vscode beeinflussen?
-
Ich kenne deinen Code nicht, aber eigentlich sollte VS in einer JS-Datei keine Variablen sehen, die einer anderen Datei definiert wurden, egal wo sie liegt.
Dazu gibt es zwei Ausnahmen:
1. export/import durch Module
2. im globalen scope deklarierte Variablen (declare global { … })
Ich vermute dein Problem liegt an Punkt 2. Wenn die Skripte nichts miteinander zu tun haben und beide im globalen scope eine Variable unterschiedlich definieren, musst du dein Projekt möglicherweise anders strukturieren. Z.b. mit den Arbeitsbereichen, aber die hab ich selbst noch nicht benutzt und weiß nicht wie sich das verhält.
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann?
-
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann? `
Am schnellsten als Bild:
Im Verzeichnis Skripte liegen:
javascript.d.ts
javascript-global.d.ts
test.js
Test/test2.js
Der Inhalt javascript-global.d.ts von steht zwei Posts drüber (viewtopic.php?f=21&t=11777&p=139202#p139128).
test.js
/// <reference path="javascript.d.ts"> /// <reference path="javascript-global.d.ts"> // @ts-check // Datei: Skripte/test.js var hallo = "text";</reference></reference>
Test/test2.js
`/// <reference path="../javascript.d.ts"> /// <reference path="../javascript-global.d.ts"> // @ts-check // Datei: Skripte/Test/test2.js var hallo = [];[/code]</reference></reference>` Das Problem ist nicht nur, dass ich die in unterschiedlichen Skripten die Variablen anders verwenden (mal als String, mal als any (leeres Array))... (da könnte ich mir noch mit einer anderen Namensgebung helfen - Bezeichnung der Variablen) ...schlimmer ist, dass in einem anderen Skript deklarierte Variablen im neuen Skript nicht angemeckert werden, wenn sie dort vergessen wurden.
-
Kannst du mal ein Minimalbeispiel (Ordnerstruktur mit Dateiinhalten) erzeugen, damit ich dein Problem nachvollziehen kann? `
Und ein Minimalbeispiel für eine fehlende Meldung bei fehlender Deklaration:
filename="Beispiel Typeprüfung 2.png" index="0">~~
-
Hier die Erklärung für das Verhalten.
https://www.typescriptlang.org/docs/han … dules.html
` > Starting with ECMAScript 2015, JavaScript has a concept of modules. TypeScript shares this concept.Modules are executed within their own scope, not in the global scope; this means that variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported using one of the export forms. Conversely, to consume a variable, function, class, interface, etc. exported from a different module, it has to be imported using one of the import forms.
Modules are declarative; the relationships between modules are specified in terms of imports and exports at the file level.
Modules import one another using a module loader. At runtime the module loader is responsible for locating and executing all dependencies of a module before executing it. Well-known modules loaders used in JavaScript are the CommonJS module loader for Node.js and require.js for Web applications.
In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well). `
Da kannst du leider fast nichts dran ändern, das ist eine TypeScript-Eigenheit. Du kannst aber erzwingen, dass eine JS-Datei als Modul gehandhabt wird. Das geht fast genauso wie ich in der .d.ts-Datei gemacht habe, mit einem Dummy-Import (bzw. require).
-
Hier die Erklärung für das Verhalten.
Da kannst du leider fast nichts dran ändern, das ist eine TypeScript-Eigenheit. Du kannst aber erzwingen, dass eine JS-Datei als Modul gehandhabt wird. Das geht fast genauso wie ich in der .d.ts-Datei gemacht habe, mit einem Dummy-Import (bzw. require).
Unbenannt.PNG `
Ich danke Dir für Deine Mühe und Geduld! Wahnsinn!
Vor allem für die mitgelieferte Erklärung :!: :idea:
Werde dass dann mit dem "require("fs");" Dummy umgehen.
Du bist doch bestimmt tausendmal intensiver mit vscode unterwegs.
Organisierst Du Dein Skriptverzeichnis anders oder nutzt Du eine Namensgebung, mit der das nicht passieren kann?
Ich habe das Gefühl, bei mir ist noch viel Luft nach oben :mrgreen:
-
Bei dem Thema Typprüfung lass ich nicht locker :mrgreen:
Das Thema bringt mich einen Riesenschritt weiter, läuft aber an vielen Stellen noch nicht rund.
Das nächste "Problem".
Die Infos für die eigenen globalen Funktionen erscheinen erst, wenn der erste Parameter begonnen wurde zu tippen.
Bei den Funktionen aus dem Javascript-Adapter kommt die Hilfe direkt nach der Klammer.
Wie es jetzt bei mir ist, ist es nur bedingt hilfreich, siehe:
-
Hmm dazu müsste ich deine global-ts-Datei mal sehen. Eigentlich müsste das gleich funktionieren. Bei mir gehts:
Auf jeden Fall kannst du per STRG+Leertaste das Fenster auch manuell öffnen. -
Hmm dazu müsste ich deine global-ts-Datei mal sehen. Eigentlich müsste das gleich funktionieren. Bei mir gehts:
Auf jeden Fall kannst du per STRG+Leertaste das Fenster auch manuell öffnen. `
Die global-ts:
! ````
import child_process = require("child_process");
! // tslint:disable:no-namespace
declare global {namespace iobGlobal {
! type loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error";
}
! // Logikwerte:
// ===========================================
/**
* true/false als Ergebnis ob der aktuelle Tag ein Arbeitstag ist
/
function arbeitstag(): boolean;
! /*
* true/false als Ergebnis ob der aktuelle Tag ein Urlaubstag ist
/
function urlaubstag(): boolean;
! /*
* true wenn die aktuelle Zeit im angegebenen Zeitraum liegt
* @param strLower Startzeit formatiert als hh:mm:ss
* @param strUpper Endzzeit formatiert als hh:mm:ss
/
function isTimeInRange(strLower:string, strUpper:string): boolean;
! // Werte ausgeben
// ===========================================
/*
* gibt den Wochentag (Montag, Dienstag, ...) als Text zurück
/
function wochentag(): string;
! /*
* Gibt das Delta zwischen einem im Datenpunkt gespeicherten
* Epoch Wert und der aktuellen Zeit formatiert zurück.
* Der Datenpunkt wird danach aktualisiert.
* Zum speichern einer Zeit in einen Datenpunkt kann die Funktion
* dateEpochNow() verwendet werden.
* @param datenpunkt ID, in der die letzt Änderung im Epoche steht.
/
function deltazeit(datenpunkt:string): string;
! /*
* gibt das aktuelle Datum & die Zeit formatiert aus
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param format Format der Ausgabe, Format wie bei formateDate()
* @param msBool true: ms im Format .ms werden angehängt
/
function dateNow(format?:string,msBool?:boolean): string;
! /*
* Gibt die Anzahl der gefundenen Subscriptions zurück.
* Listet alle Subscriptions innerhalb der Javascript-Instanz auf, die dem Filter entsprechen, im Log auf.
* Defaultformat: "YYYY-MM-DD hh:mm:ss"
* @param scriptname (optional) "alle", "Name des Skripts", "FEQ", usw.
* @param level (optional) loglevel für logs, default: "info"
* // Filter (scriptname):
* --------------------
* sucht den String in den überwachten Datenpunkten der Javascript-Instanz
* und sucht den String in den Scriptnamen innerhalb der Javascript-Instanz
* keine Angabe -> Filter = "alle" (alle Subscriptions der Instanz)
*
* loglevel
* --------
* loglevel = "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
*
* Beispiele:
* ----------
* prinSubs(); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* prinSubs("alle"); listet alle Subscriptions innerhalb der Javascript-Instanz auf
* printSubs("Skriptname"); alle Subscriptions innerhalb des aufrufenden Scripts
* printSubs("FEQ"); alle Subscriptions, die im Script oder Objekt "FEQ" enthalten
/
function printSubs(scriptname?:string,level?:iobGlobal.loglevel): number;
! /*
* Gibt eine zufällige Zahl zwischen min und max zurück
* @param min kleinster möglicher Wert der Zufahlszahl
* @param max größter möglicher Wert der Zufahlszahl
/
function rand(min:number,max:number) : number;
! // Funktionen
// ===========================================
! /*
* Rundet eine Zahl auf die angegebenen Stellen
* @param wert Zahl, die gerundet werden soll
* @param stellen Anzahl der Stellen
/
function runden(wert: number, stellen: number): number;
! /*
* Ansage über den sayit Adapter
* @param text Ansage, die abgespielt werden soll
* @param vol (optional) Lautstärke der Ansage von 1-100
* @param instanz? (optional) ohne Angabe: default Instanz. Mit Angabe: sayit Instanz
*/
function sayit(text: string, vol?: number, instanz?: number): void;/** * formatiert eine Übergabe in Sekunden zu einem lesbaren String * @param sekunden Sekunden, die lesbar ausgegeben werden sollen */ function sek2txt(sekunden:number | string): string;
! /**
* Loglevel innerhalb eines Javascripts
* das Script muss am die Variable des gewünschten Loglevels definiert haben, z.B.:
* var loglevel = "info";
* @param logtext Text, der in der Logausgabe erscheinen soll
* @param level "debug2" | "debug1" | "debug" | "info" | "warn" | "error"
* @param color? (optional) ohne Angabe: vordefinierte Farbe für den level, mit Angabe: gültige html Farbe
* Rückgabe: verwendeter Loglevel zur Information
/
function logs(logtext:string,level:iobGlobal.loglevel,color?:string):string;
! /*
* Schreibt einen Text mit Zeitstempel in eine Logdatei
* Datei: /opt/iobroker/iobroker-data/scripts-logf.log
* @param text Text, der mit einem Zeitstempel in die Logdatei geschrieben werden soll
/
function logf(text:string);
! /*
* Fügt einen Eintrag (Text) einer Ereignisliste hinzu
* @param text Eintrag, der der Liste hinzugefügt werden soll. Formatierungen mit html Tags möglich
* @param list (optional) Liste, in der der Eintrag erscheinen soll. Ohne Angabe: Liste "default"
* @param color (optional) wenn die ganze Zeile mit einer html Farbe eingefärbt werden soll
/
function event(text: string, list?: string, color?: string) : void;
! // Helfer:
// ===========================================
/*
* liefere Anzahl n nbsp in utf-8, wenn str nicht angegeben oder n-mal str
* @param n Anzahl von nbsp;, bzw. wenn str angegeben: ANzahl des Strings in str
* @param str (optional) String, welche n mal erzeugt wird
/
function fill(n:number,str?:string): string;
! /*
* gibt das aktuelle Datum in Epoch zurück
/
function dateEpochNow() : number;
! /*
* entfernt aus einem html String die html Tags
*/
function html2str(html:string) : number;
! // Workaround, Fehler in der ioBroker Typprüfung
// ===========================================
function getSubscriptions(): any;
! const name: string;
! }
! ````STRG+Leertaste probiere ich direkt
-
So etwas getestet.
Neuer Ordner, zwei Testdateien angelegt (test.js und test.d.ts).
Es verhält sich schon anders als bei Dir, aber besser als in meiner Arbeitsumgebung.
Die Funktion wird während des tippens nicht vorgeschlagen.
Erst, wenn ich "Klammer auf" drücke erscheint die Beschreibung.
-
Die gleichen Dateien in einem Arbeitsordner in meinem Skript Arbeitsverzeichnis:
Wenn ich nun die "Klammer auf drücke, wird abc gegen AbortController( ersetzt.
Wenn ich das nicht will, muss ich direkt nach abc die [ESC] Taste drücken.
Du hast require("fs"); in der *.d.ts Datei gesetzt. Reicht das aus Deiner Sicht dort?
Ich hätte das jetzt bei jedem Skript davor gesetzt.
-
In meinem Arbeitsbereich verhält sich vscode total schizophren :shock:
tippe ich abc ein kommen andere Vorschläge.
Ich muss ESC drücken, damit mit der Klammer auf das abc nicht durch einen Vorschlag ersetzt wird.
In diesem Moment erscheint der Hilfetext zur Funktion abc().
Gleichzeitig wird abc unterstrichen und angemeckert, dass die Funktion unbekannt ist.
Verlasse ich einmal die Klammern () verschwindet der Hilfetext und tauch auch nicht mehr auf.
-
Schau dir mal meinen Screenshot bzw. javascript.d.ts genau an. Auch diese Datei muss in ein Modul "gezwungen" werden. Anders als bei JS reicht require nicht aus, es muss das import keyword fallen.
// entweder so import * as fs from "fs"; // oder so import fs = require("fs");