Typescript: Ermöglichen Sie die Sichtbarkeit von Konstruktoren

Erstellt am 13. März 2015  ·  42Kommentare  ·  Quelle: microsoft/TypeScript

Ich denke, es ist ein ziemlich verbreitetes Muster, eine statische Factory-Methode zum Erstellen einer Klasse zu haben, und der Konstruktor dieser Klasse ist ansonsten privat, sodass Sie die Klasse nur instanziieren können, wenn Sie die Factory-Methode verwenden.

Fixed Suggestion help wanted

Hilfreichster Kommentar

Das gilt auch für private Funktionen im Unterricht, oder? Ich verstehe nicht, warum wir keinen Kompilierungsfehler für den Zugriff auf einen privaten Konstruktor erhalten konnten.

Alle 42 Kommentare

Da alle "Klassen" eigentlich nur Funktionen sind und es keine nicht aufrufbare Funktion gibt, können Sie überall dort, wo die Klasse sichtbar ist, auch den Operator new .

Sie können die Klasse jedoch nicht öffentlich machen (z. B. in einem Modul) und eine Schnittstelle für die Klasse exportieren. Dies missbraucht die Tatsache, dass Schnittstellen Klassen erweitern können, aber weder direkt instanziierbar noch erweiterbar sind.

Das gilt auch für private Funktionen im Unterricht, oder? Ich verstehe nicht, warum wir keinen Kompilierungsfehler für den Zugriff auf einen privaten Konstruktor erhalten konnten.

@billccn Ich mag es nicht, Anfragen von "JS erlaubt, dann kannst du dich nicht verstecken" zu töten.
Eine Sache ist, in TS & generiertem JS vollständig zu schützen, eine andere Sache ist, dies bis zum Generieren von JS zu schützen. Wie Sie erklärt haben, ist der vollständige Schutz nicht möglich, es sollte jedoch möglich sein, dass der Compiler eine andere Sichtbarkeit überprüft.
Wenn Sie keine Sichtbarkeitsmodifikatoren mögen, verwenden Sie überall Standard-Public. Es gibt andere, die dieses Konzept nützlich finden.

Ja, ich denke, wenn die privaten Felder nur als Compiler-Check implementiert sind, kann sie wahrscheinlich auf Konstruktoren erweitert werden. Die schnittstellenbasierte Problemumgehung funktioniert jedoch bereits.

: +1: Es gibt Zeiten, in denen ich den Programmierer zwingen möchte, die Factory-Methoden für die Lesbarkeit des Codes zu verwenden. Die schnittstellenbasierte Problemumgehung verursacht viel Rauschen im Code.

Ich denke, nur der Compiler-Check ist der richtige Weg.

Akzeptiert, PRs akzeptieren

Könnte eine Klasse mit einem privaten Konstruktor erweiterbar sein?

dh würde dies einen Fehler auslösen?

class A {
    private constructor() {
    }
}

class B extends A { // Should there be an error at A saying: "Cannot extend private class 'A'"?
}

Wenn ja, würden wir dies dann zulassen:

class A {
    protected constructor(a?: any)
    private constructor() {

    }
}

class B extends A { // No error since 'A' has a non-private constructor
}

Aus der Erfahrung von Nicht-JS-Entwicklern ist dies das erwartete Verhalten.

Im ersten Beispiel sollte B ein Fehler sein, da sein impliziter Superaufruf illegal ist. Eine Klasse mit einem private constructor ist also effektiv sealed / final .

Im zweiten Beispiel sollte die Deklaration von A ein Fehler sein, da alle Überladungen eines Konstruktors und seiner Implementierung dieselbe Sichtbarkeit haben müssen (dieselbe Regel, die wir für Methoden haben).

Siehe auch # 471. Ist es wirklich erforderlich, Konstruktoren privat zu lassen, oder ist dies geschützt?

@benliddicott Manchmal ist es nützlich, einen Singleton zu erzwingen oder den Programmierer zu zwingen, eine der statischen Methoden zum Erstellen eines Objekts zu verwenden, da es manchmal besser lesbar sein kann.

Siehe hier .

@dsherret protected erfüllt all diese Anforderungen.

Sie können jedoch nicht sicher sein, dass ein nachgeschalteter Benutzer niemals ein legitimes Bedürfnis hat, von Ihrer Klasse zu erben. Der einzige Effekt von private besteht darin, zu verhindern, dass Ihre nachgeschalteten Benutzer einen Bedarf erfüllen, den Sie nicht erwartet haben.

@benliddicott Manchmal ist das einzige, was Sie wollen, dass eine Klasse nicht erweiterbar ist. Ich verweise Sie auf Effective Java Item 15 Minimize Mutability, insbesondere:

"2. Stellen Sie sicher, dass die Klasse nicht erweitert werden kann. Dies verhindert, dass unachtsame oder böswillige Unterklassen das unveränderliche Verhalten der Klasse beeinträchtigen, indem sie sich so verhalten, als hätte sich der Status des Objekts geändert. Das Verhindern von Unterklassen wird im Allgemeinen erreicht, indem die Klasse endgültig gemacht wird, aber dort ist eine Alternative, die wir später diskutieren werden.

Derzeit gibt es in TypeScript keine Unterstützung für final / sealed Daher ist ein privater Konstruktor der einzige Weg, um eine unveränderliche Klasse aus Sicht des Typsystems zu erreichen. (Ich empfehle jedoch, dass Benutzer das Objekt auch im Konstruktor einfrieren.)

@ Billccn , die Meinung dieses Autors ist interessant. So ist die Idee, dass die Meinung des Bibliotheksautors die des Bibliotheksbenutzers überschreiben sollte. Meine eigene Erfahrung hat gezeigt, dass Bibliotheksautoren nicht wissen, wann sie privat verwenden sollen, und dass sie es übermäßig nutzen, was den Benutzern Kopfschmerzen bereitet, einfach weil sie glauben, zu wissen, wie ihre Bibliothek verwendet wird, obwohl sie dies nicht tun.

Anstelle einer statischen Sprache wie Java wäre Perl, eine andere dynamische Sprache, ein passenderer Vergleich: http://www.perlmonks.org/?node_id=437623

Einer der Gründe, warum Perl keine Zugriffsmodifikatoren wie öffentlich, privat und geschützt hat, ist, dass erkannt wird, dass sie häufig die Erledigung der Aufgabe behindern: Was der ursprüngliche Designer sich vorgestellt hat, hat nichts damit zu tun was du damit machen willst. Auf die gleiche Weise, Design für Flexibilität - obwohl Sie vielleicht nicht sehen, dass Sie es jetzt brauchen, kann es für die nächste Person unglaublich praktisch sein, dieses neue Problem zu lösen, und Ihr Genie für die Entwicklung dieser Flexibilität segnen ;-)

und:

Perl hat keine Leidenschaft für erzwungene Privatsphäre. Es wäre besser, wenn Sie sich aus dem Wohnzimmer heraushalten würden, weil Sie nicht eingeladen wurden, nicht weil es eine Schrotflinte hat

http://www.perlmonks.org/?node_id=1096925

Tatsächlich ist JavaScript in fast jeder Hinsicht dasselbe und - ja - TypeScript ist dasselbe. In Typoskript können Sie problemlos auf private Mitglieder zugreifen - mit der treffend benannten Escape-Luke: obj["propertyName"] .

Wenn Sie als Bibliotheksschreiber den Verdacht haben, dass es unklug ist, eine Methode aufzurufen oder von einem Objekt zu erben, teilen Sie dem Benutzer mit, dass dies unklug ist. Aber verhindern Sie sie nicht - sie wissen es vielleicht doch besser als Sie.

Ich verstehe die Diskussion über "geschützt ist ausreichend" nicht. Wenn TS das Konzept der Sichtbarkeit hat und ich dieses Konzept auf Konstruktoren anwenden kann, lautet die Antwort "es ist nicht ausreichend".

Wenn Zugriffsmodifikatoren für den Konstruktor zulässig sind, sollte er meiner Meinung nach mit anderen Modifikatoren konsistent sein und privat zulassen.

Private Mitglieder im Allgemeinen sind auf jeden Fall nützlich. Mit ihnen können Sie die Implementierungsdetails einer Klasse organisieren und umgestalten, ohne sich Gedanken über Nebenwirkungen außerhalb der Klasse machen zu müssen.

Mit privaten Konstruktoren möchte ich möglicherweise die Entwickler in meinem Team dazu zwingen, auf eine bestimmte Weise zu programmieren. Zum Beispiel möchte ich sie möglicherweise zwingen, die statische Methode hier zu verwenden, da sie besser lesbar ist, und sie zwingen, diese Klasse nicht zu erweitern:

class Currency {
    private constructor(private value: number, private type: CurrencyType) {
    }

    static fromNumberAndType(value: number, type: CurrencyType) {
        return new Currency(value, type);
    }

    static fromString(str: string) {
        const value = ...,
              type  = ...;

        return new Currency(value, type);
    }

    // ... omitted ...
}

// error:
const badCurrency = new Currency(5.66, CurrencyType.USD);
// ok:
const goodCurrency1 = Currency.fromNumberAndType(5.66, CurrencyType.USD);
const goodCurrency2 = Currency.fromString("5.66 USD");

Mit privaten Konstruktoren möchte ich möglicherweise die Entwickler in meinem Team dazu zwingen , auf eine bestimmte Weise zu programmieren.

Das ist ein Managementproblem, kein Sprachdesignproblem.

@benliddicott Ähnliches können Sie über
Verwenden Sie TS und erstellen Sie einige fusselartige Regeln, die die Verwendung von private on constructor verbieten. Um Ihren letzten Kommentar zu paraphrasieren: "Das ist ein Tooling-Problem, kein Sprachdesign-Problem".

@benliddicott Wenn etwas nicht möglich ist, muss ich es nicht zurückschicken, wenn es nach einer

Dem Compiler genau zu sagen, wie der Code richtig verwendet werden soll, ist ein Vorteil, der dem Entwickler, der ihn während der Programmierung verwendet, das richtige Feedback gibt.

@dsherret Nein, es ist eine willkürliche Einschränkung, die Architecture astronauts : -1:

@jbondc Ist das "Architekturastronauten" ein vernünftiges Argument? Sie versuchen, die Leute zu beleidigen oder zu loben, die diese Funktion wollen?

@jbondc Ich glaube nicht, dass der Begriff "Architekturastronaut" hier relevant ist. Handelt es sich nicht um Menschen, die mehr Zeit damit verbringen

Außerdem halte ich es nicht für "willkürlich", da es dazu beitragen kann, den Missbrauch des Codes zu verhindern. Vielleicht möchte ich mich oder mein Team daran erinnern, den Konstruktor nicht zu verwenden und stattdessen eine der statischen Methoden zu verwenden oder die Verwendung einer statischen Methode zu erzwingen, um einen Singleton zu erzwingen. Es ist schnell und einfach und ich bekomme das richtige Feedback vom Compiler. Wenn das willkürlich ist, könnte man argumentieren, dass viele Aspekte einer Sprache willkürlich sind. Vielleicht haben Sie mehr darüber zu sagen, dass es willkürlich ist, was Sie in Ihrem Kommentar nicht ausgedrückt haben?

Dies ist eine Sprachfunktion, die von Menschen, die sie nicht mögen, einfach nicht verwendet werden kann.

@dsherret Nicht genug, um zu dokumentieren, anstatt noch eine weitere Einschränkung durchzusetzen?

Dies erschwert die Mehrfachvererbung erheblich, siehe # 4805 (was mir momentan wie ein nachträglicher Gedanke erscheint). Ich habe bereits einige meiner Gedanken in # 3578 zum Ausdruck gebracht, werde mich also nicht die Mühe machen, es noch einmal zu tun. Kam mit astronaut ein bisschen stark raus, will niemanden beleidigen.

@ jbondc- Klassen mit privaten Konstruktoren können nicht vererbt werden. Sie sind im Wesentlichen versiegelt und daher sollte die Vererbung, geschweige denn die Mehrfachvererbung, damit nicht funktionieren.

Es ist viel schöner, selbstdokumentierenden Code zu haben, als ihn extern oder in einem Kommentar zu schreiben. Dies ist einer der Gründe, warum mir alle Einschränkungen von TypeScript gefallen und im Wesentlichen das, wonach wir in dieser Funktion fragen - nur eine weitere Möglichkeit, den Code mithilfe der Sprache selbst zu dokumentieren.

Nun, hier ist eine andere Möglichkeit, Ihr Beispiel zu schreiben:

module Currency {
    export enum Type {
        CAD = 1.3,
        USD = 1,
    }

    class PrivateCurrency {
        constructor(private value: number, private type: Type) { }
    }

    export function fromNumberAndType(value: number, type: Type) {
        return new PrivateCurrency(value, type);
    }

    export function fromString(str: string) {
        let value = 10;
        let type = Type.CAD;
        return new PrivateCurrency (value, type);
    }
}

Weniger OO-ish, aber Sie haben tatsächlich eine "wirklich" echte Privatklasse.

@jbondc Schön, dass du deinen Weg für Privatstunden gefunden hast! Lassen Sie andere einen anderen Ansatz verwenden (exportierbare Klasse mit privatem Konstruktor). Jetzt können Sie feststellen, dass es Funktionen geben kann, die die Anforderungen von Gruppe A erfüllen und die Arbeit von Gruppe B nicht stören. :) :)

+1

+1

Ist das noch auf dem Radar? Die PR ist hella abgestanden!

+1

+1

+1

: +1: kann für das Werksentwurfsmuster nützlich sein

Entschuldigung, wenn dies eine Wiederholung oder nur zu spät für die Party ist, aber wir verwenden das folgende Muster, um private Konstruktoren zu erreichen:

interface ExampleBuilder {
    Instance(): Example;
}

export interface Example {
    Val(): number;
}

export let Example: ExampleBuilder = class ExampleImpl {
    constructor(v: number) {
    }

    Val(): number {
        return 42;
    }

    static Instance(): Example {
        return new ExampleImpl(2);
    }
};

let x = Example.Instance(); // OK: x has type Example
let y = new Example(5);     // ERROR: Cannot use 'new' with an expression whose type lacks a call or construct signature.

Beachten Sie, dass dies mit dem kürzlich zusammengeführten Modifikator readonly noch mehr aufgeräumt werden kann.

@myitcv das ist ziemlich cool, um es

Persönlich markiere ich nur alle meine zukünftigen privaten Konstruktoren mit // todo: make private once supported Kommentaren und rufe die Konstruktoren nicht auf. Sobald diese Funktion verfügbar ist, wird es hilfreich sein, mit einem Zugriffsmodifikator eine gewisse Durchsetzung und bessere Dokumentation zu erhalten.

@dsherret

Es ist immer noch zu ausführlich und es dauert ein paar Sekunden, um zu verstehen, was los ist

Einverstanden. Wir leiden nicht zu sehr unter dieser kognitiven Belastung, da diese Klassen durch Code generiert werden. Die Schnittstelle zum Programmierer ist also eigentlich schön und einfach.

behoben durch https://github.com/Microsoft/TypeScript/pull/6885

danke @AbubakerB!

Hallo, wird diese Funktion in einer zukünftigen Version von Typoskript veröffentlicht?
Der Versuch, einen privaten oder geschützten Konstruktor zu deklarieren, führt ab sofort zu diesem Fehler in Typoskript 1.8.10:
Fehler TS1089: Der Modifikator 'private' kann in einer Konstruktordeklaration nicht angezeigt werden.

Ahh, egal. Ich habe gerade die Roadmap herausgefunden, die besagt, dass diese Funktion in Typoskript 2.0 enthalten sein wird.

Ahh, egal. Ich habe gerade die Roadmap herausgefunden, die besagt, dass diese Funktion in Typoskript 2.0 enthalten sein wird.

Auch der Meilenstein, der auf TypeScript 2.0 und das Label Fixed würde anzeigen, dass er enthalten ist. Alles mit einem Label von Fixed ist im Allgemeinen im Master enthalten und über npm install typescript@next .

Ich verwende TypeScript 2.0.2 RC und erhalte immer noch TS1089, wenn ich versuche, einen private -Konstruktor zu erstellen. Mache ich es falsch oder ist das einfach nicht behoben?

Ich verwende TypeScript 2.0.2 RC und erhalte immer noch TS1089, wenn ich versuche, einen privaten Konstruktor zu erstellen. Mache ich es falsch oder ist das einfach nicht behoben?

es funktioniert für mich. Stellen Sie sicher, dass Ihr Befehlsalias auf die richtige Version verweist und dass Ihr Editor aktualisiert wurde, um die neueste TS-Version zu verwenden.

Ich habe das Problem gefunden. Es war die Schuld von gulp-typescript die trotz meiner Angaben in package.json und der Lösung für gulp-typescript die falsche Version von tsc PATH .

Für alle anderen, die dieses Problem haben, bestand meine Lösung darin, meine gulpfile.js und ...

  1. require TypeScript vor gulp-typescript wie folgt:
// Tool-Chain: Scripts
var tsc = require("typescript");
var typescript = require('gulp-typescript');
  1. Stellen Sie den Compiler als Override bereit, wenn Sie meine Build-Aufgabe definieren:
// Task(s): Build TypeScript Outputs
var tsconfig = typescript.createProject("path to tsconfig", { typescript: tsc });
gulp.task('build:scripts', function () {
    let ts = tsconfig.src()
                     .pipe(sourcemaps.init())
                     .pipe(typescript(tsconfig));

    return ts.js.pipe(sourcemaps.write(".")).pipe(gulp.dest(path.join(outputs.root, outputs.scripts)))
});
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen