Ich mag statt unveränderlicher Schnittstellen unveränderliche Klassen verwenden, aus zwei Gründen:Typoskript - Wie manipuliere ich unveränderliche Klassen?
- so, dass ich mit ihnen einen Code bündeln kann (wie die
doStuff
Methode) - „fremde Eigenschaften“, um nicht zuzulassen (siehe Beispiel unten: egal wo es gültig ist
otherThing
zu setzen, mag ich, dass ein Compiler-Fehler) mit den meist tut zu kommen
Dies ist die nächste, die ich habe in der Lage gewesen, um zu bewirken, was ich will:
interface IData4 {
readonly thing1: string;
readonly thing2: string;
}
class Data4 implements IData4 {
readonly thing1: string;
readonly thing2: string;
constructor(that: IData4, props?: Partial<IData4>){
Object.assign(this, that);
if(props){
Object.assign(this, props);
}
}
doStuff(){
return this.thing1 == this.thing2;
}
}
// more verbose than a normal ctor parameter list, but I like it better anyway
// more readable, and it makes transposition errors less likely when all
// the params are the same type
// I write code to create instances rarely, reading and updating is more frequent
let data4 = new Data4({thing1: "t1", thing2: "t2"});
// GOOD!: error because of "otherThing"
// let other = new Data4({thing1: "t1", thing2: "t2", otherThing: 'blah'});
// GOOD!: error because missing "thing2"
// let other = new Data4({thing1: "t1"});
// this is the usual update case
let data4a = new Data4(data4, {thing2: "t2a"});
log.debug("data4a: " + JSON.stringify(data4a));
// GOOD! error because of "otherThing"
// let other = new Data4(data4, {thing2: "t2b", otherThing: 'blah'});
// BAD! want "otherThing" to cause error
// but I'm unlikely to use this construct, I wouldn't specify the spread
// operator again because I already specified the thing to copy from
let bad2b = new Data4(data4, {...data4, thing2: "t2b", otherThing: 'blah'});
// BAD! want "otherThing" to cause error
// easy to do accidentally because I'm used to using interfaces
let bad2c = new Data4({...data4, thing2: "t2b", otherThing: 'blah'});
let iData: IData4 = {thing1: 't1', thing2: 't2'};
// BAD! want an error about "otherThing"
let iData2 = {...iData, thing2: 't2a', otherThing: 'blah'};
// GOOD! error because of "otherThing"
// let other: IData4 = {thing1: 't1', thing2: 't2', otherThing: 'blah'};
// GOOD! error because lack of "thing2"
// let other: IData4 = {thing1: 't1'};
Probleme mit diesem Konstrukt:
- das gesamte Interface + Klasse Konstrukt ist unhandlich und die Eigenschaftsdefinitionen Duplizieren ist albern (plus fehleranfällig)
- es mir noch fremden Eigenschaften angeben können, wenn ich die Verwendung "Spread" -Operator
Also, gibt es einen besseren Weg, dies zu tun?
- Gibt es eine Möglichkeit, Funktionen mit Schnittstellen wie gewünscht zu verpacken?
- Oder gibt es einen besseren Typ als "Partial", den ich verwenden sollte?
- Gibt es einen Weg, ungefähr die gleiche Funktionalität wie bei Partial zu erreichen, ohne jedoch die Einstellung von Fremdeigenschaften zuzulassen?
- Oder gibt es einen Weg in Typescript, wo ich normale Konstruktoren mit Parameterlisten verwenden kann, um ein neues Objekt mit einer geänderten Eigenschaft zu erstellen, aber nicht jede Eigenschaft angeben müssen?
- Insbesondere, ich will nicht zu tun:
let data4a = new Data4(data4.thing1, "t2a"});
- Insbesondere, ich will nicht zu tun:
Die meisten funktionalen Sprachen mit einem Schwerpunkt auf Unveränderbarkeit haben ein Konstrukt, das eine neue Kopie eines Objekts erstellt, wobei einige Felder geändert werden, z. B. [F # ''' '' '' '' '' '' '' '' '.com/de-de/dotnet/articles/fsharp/sprachreferenz/copy-und-update-record-ausdrücken). JS/TS sind keine funktionalen Sprachen mit einem Schwerpunkt auf Unveränderbarkeit. Sie können es mit 'WithX (x: X)' Methoden emulieren, aber das wird mühsam, wenn Ihr Objekt viele Eigenschaften hat. Einige Projekte (z. B. der C# -Compiler) (https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Syntax/Syntax.xml) verwenden dafür Codegeneratoren –