NEWS
TypeScript und common.states (Record<string, string>)
-
Ich habe gesehen, dass common.states (für createState) als Record<string, string> definiert ist: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/iobroker/index.d.ts
So ganz happy bin ich mit dem Record nicht, weil man zumindest im einfachsten Fall mit Zeichenketten herum hantiert, wo natürlich schnell mal ein Tippfehler passieren kann. Außerdem hat man keine Autovervollständigung, wenn man mit der Variable weiterarbeitet.
const stateValues1: Record<string, string> = { stateA: 'State A', stateB: 'State B', } var stateId1 = 'javascript.0.state1'; createState(stateId1, 'stateA', false, { name: 'Status 1', type: 'string', read: true, write: false, unit: '', role: 'state', states: stateValues1 }); var value1 = getState(stateId1).val; log(value1.toString()); var someVar1 = 'stateB'; if(value1 == someVar1) {} var someVar1b = 'sateB'; // Typo if(value1 == someVar1b) {} setState(stateId1, value1)
Man könnte auch mit Konstanten arbeiten, aber Autovervollständigung hat man dann bei den States nicht:
const state3A = 'stateA'; const state3B = 'stateB'; type State3 = 'stateA' | 'stateB'; const stateValues3: Record<State3, string> = { [state3A]: 'State A', [state3B]: 'State B', } var stateId3 = 'javascript.0.state3'; createState(stateId3, state3A, false, { name: 'Status 3', type: 'string', read: true, write: false, unit: '', role: 'state', states: stateValues3 }); var value3: State3 = getState(stateId3).val; log(value3.toString()); var someVar3: State3 = state3B; //var someVar3b: State3 = 'sateA'; <-- typo not allowed if(value3 == someVar3) {} setState(stateId3, value3);
Am besten würden mir folgende Variante mit Enums gefallen. Allerdings ist hier der State mit numerischen Keys definiert (common.type: 'number'). common.states daher als Record<number, string> statt Record<string, string>
enum States2 { stateA, stateB } const stateValues2: Record<States2, string> = { [States2.stateA]: 'State A', [States2.stateB]: 'State B' } var stateId2 = 'javascript.0.state2'; createState(stateId2, States2.stateA, false, { name: 'Status 2', type: 'number', read: true, write: false, unit: '', role: 'state', states: stateValues2 }); var value2: States2 = getState(stateId2).val; log(value2.toString()); var someVar2 = States2.stateB; if(value2 == someVar2) {} setState(stateId2, value2);
Es ginge zwar mit Enums auch mit Record<string, string> - das wird aber kompliziert:
enum States4 { stateA, stateB, } const stateValues4: Record<string, string> = { [States4[States4.stateA]]: 'State A', [States4[States4.stateB]]: 'State B' } var stateId4 = 'javascript.0.state4'; createState(stateId4, States4[States4.stateA], false, { name: 'Status 4', type: 'string', read: true, write: false, unit: '', role: 'state', states: stateValues4 }); var value4: States4 = States4[getState(stateId4).val as string]; log(value4.toString()); var someVar4: States4 = States4.stateB; if(value4 == someVar4) {} setState(stateId4, States4[value4]);
Ich bin keine Typescript-Profi - vielleicht gibt's noch bessere Varianten. Ich finde es halt am schönsten, wenn man so States im Code als Enums definiert, weil man dann keine Tippfehler machen kann und Autovervollständigen hat.
Ist es ein Problem, wenn der common.state als Record<number, string> und der State als "number" definiert ist? -
@noox
common.states ist als Objekt mit einer Anzahl diskreter Werte (false/true oder 0/1/2/...) denen Zustandstexte zugewiesen werden, definiert. Was soll daran geändert werden ? -
@noox sagte in TypeScript und common.states (Record<string, string>):
So ganz happy bin ich mit dem Record nicht, weil man zumindest im einfachsten Fall mit Zeichenketten herum hantiert, wo natürlich schnell mal ein Tippfehler passieren kann.
Tippfehler abfangen in Objekten, die an Funktionen übergeben werden (wie der common-Teil an sich), funktioniert nur, wenn die Form des Objekts vorher bekannt ist. Im Falle von common.states ist das nicht gegeben, da hier jeder erdenklicher Key erlaubt ist.
Zu number vs. string: Objekte in JS haben strings als Keys, selbst wenn du mit einer Zahl drauf zugreifst:
"states": { "0": "ALL ON not active, ALL OFF not active", "1": "ALL ON not active, ALL OFF active", "2": "ALL ON active, ALL OFF not active", "255": "ALL ON active, ALL OFF active" }
Deswegen ist bei
Record<string, ...>
auch der Zugriff mit numerischen Keys möglich:
https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZiEAuGAlApqATgEwDzRYCWYA5gDQwBGiANugIZgB8MAvDAN4C+A3AFAIQAbQBEARlEBddjChYArugFDh4mR3lLeQA
Zweitens: Bist du gerade nicht eher beim Skripten im Skript-Adapter? ==> https://github.com/ioBroker/ioBroker.javascript/blob/master/lib/javascript.d.ts
-
@noox oder willst du erreichen, dass beim
createState
mit vorhandenemcommon.states
geprüft wird, ob der übergebene Wert dazu passt? Das könnte gehen - aber nur, wenn der zu setzende Wert genau genug bekannt ist.Für die Nutzung mit Variablen steht man schon wieder vor dem Problem, dass irgendwie die Brücke zur State-Definition geschlagen werden muss - und das ohne den Code auszuführen.
-
@paul53 said in TypeScript und common.states (Record<string, string>):
@noox
common.states ist als Objekt mit einer Anzahl diskreter Werte (false/true oder 0/1/2/...) denen Zustandstexte zugewiesen werden, definiert. Was soll daran geändert werden ?Die common.states können in JS ja auf verschiedensten Weise angegeben werden. In TypeScript ist zumindest die Definition mit
Record<string, string>
restriktiver. Mir geht's nicht um eine Änderung, sonder eher um eine Best Practice.Mir ist nämlich mehrmals aufgefallen, dass ich mich verhaut habe. Z.B. habe ich einen Record standardmäßig definiert. Man darf aber dann nicht den Fehler machen, den State mit dem Wert
stateValues1.stateA
(vom Record) zu erstellen oder auf diesen zu setzen. WeilstateValues1.stateA
liefert den Value ('Status A') und nicht den Key. Fehler bekommt man allerdings keinen - d.h. wenn man es durchgängig macht, funktioniert es sogar. -
@AlCalzone said in TypeScript und common.states (Record<string, string>):
Tippfehler abfangen in Objekten, die an Funktionen übergeben werden (wie der common-Teil an sich), funktioniert nur, wenn die Form des Objekts vorher bekannt ist. Im Falle von common.states ist das nicht gegeben, da hier jeder erdenklicher Key erlaubt ist.
Mir geht's darum, dass ich den Wert eines Status dann in einer vernünftigen Form habe - eben z.B. ein Enum. Damit dann die weitere Fehleranfälligkeit geringer ist. Dass die States alle möglichen Werte akzeptieren, finde ich nicht so tragisch - insbesondere wenn ich eben vorher mit sauberen Typen bzw. Enums arbeiten kann, dann ist die Wahrscheinlichkeit eh gering, hier einen Fehler zu machen.
Zu number vs. string: Objekte in JS haben strings als Keys, selbst wenn du mit einer Zahl drauf zugreifst:
Deswegen ist beiRecord<string, ...>
auch der Zugriff mit numerischen Keys möglich:Damit ich die Enums verwenden kann, hatte ich den Record als
Record<MyEnum, string>
definiert. Allerdings vergessen,common.type
vonstring
aufnumber
zu ändern.createState('id', MyEnum.enumValA, false, {...});
funktioniert trotzdem.setState('id', MyEnum.enumValA);
funktioniert auch, liefert allerdings eine Warnung bei der Ausführung. Was ja eigentlich auch OK ist, weil der Enum-Wert ja einenumber
ist.Prinzipiell hat man einige Freiheiten bei den States, aber es gibt halt auch ein paar Fallen. Daher möchte ich mir halt eine Best Practice-Methode zurechtlegen. Aber denke, ich bleib bei Variante 3 mit dem Enum.
Zweitens: Bist du gerade nicht eher beim Skripten im Skript-Adapter? ==> https://github.com/ioBroker/ioBroker.javascript/blob/master/lib/javascript.d.ts
Danke!
-
Das was du (glaube ich) vor hast, kann man nicht statisch prüfen. Der Aufruf von
setState('id', MyEnum.enumValA);
hat für TypeScript nichts mitcreateState('id', MyEnum.enumValA, false, {...});
zu tun.Außerdem kommt wie gesagt noch dazu, dass
Record<string, string>
ist bei den Keys auch flexibel ist -number
geht aus Kompatibilitätsgründen zu JavaScript auch. Und Enums mit TypeChecking ist auch eher so lala.
Wenn du was findest, was für dich funktioniert, dann ists gut. Aber ich fürchte das global zu nutzen wird schwierig.