Ich möchte ein Funktionsobjekt erstellen, auf dem auch einige Eigenschaften gespeichert sind. Zum Beispiel in JavaScript würde ich tun:
var f = function() { }
f.someValue = 3;
Jetzt kann ich in TypeScript den Typ wie folgt beschreiben:
var f: { (): any; someValue: number; };
Ich kann es jedoch nicht bauen, ohne eine Besetzung zu benötigen. Sowie:
var f: { (): any; someValue: number; } =
<{ (): any; someValue: number; }>(
function() { }
);
f.someValue = 3;
Wie würden Sie dies ohne eine Besetzung bauen?
var f: { (): any; someValue: number; } = <{ (): any; someValue: number; }>{ ...(() => "Hello"), someValue: 3 };
.Antworten:
Wenn die Anforderung also darin besteht, diese Funktion einfach ohne Besetzung zu erstellen und "f" zuzuweisen, ist hier eine mögliche Lösung:
var f: { (): any; someValue: number; }; f = (() => { var _f : any = function () { }; _f.someValue = 3; return _f; })();
Im Wesentlichen wird ein selbstausführendes Funktionsliteral verwendet, um ein Objekt zu "konstruieren", das mit dieser Signatur übereinstimmt, bevor die Zuweisung abgeschlossen ist. Die einzige Seltsamkeit ist, dass die innere Deklaration der Funktion vom Typ 'any' sein muss, andernfalls schreit der Compiler, dass Sie einer Eigenschaft zuweisen, die für das Objekt noch nicht vorhanden ist.
EDIT: Den Code etwas vereinfacht.
quelle
Update: Diese Antwort war die beste Lösung in früheren Versionen von TypeScript, aber in neueren Versionen stehen bessere Optionen zur Verfügung (siehe andere Antworten).
Die akzeptierte Antwort funktioniert und kann in einigen Situationen erforderlich sein, hat jedoch den Nachteil, dass keine Typensicherheit für den Aufbau des Objekts bereitgestellt wird. Diese Technik löst zumindest einen Typfehler aus, wenn Sie versuchen, eine undefinierte Eigenschaft hinzuzufügen.
interface F { (): any; someValue: number; } var f = <F>function () { } f.someValue = 3 // type error f.notDeclard = 3
quelle
var f
Zeile keinen Fehler verursacht, da zu diesem Zeitpunkt keinesomeValue
Eigenschaft vorhanden ist.var f:F = function(){}
Dies schlägt im obigen Beispiel fehl. Diese Antwort eignet sich nicht für kompliziertere Situationen, da Sie die Typprüfung in der Zuweisungsphase verlieren.<F><any>function() { ... }
var f = (function () {}) as any as F;
Dies ist jetzt leicht erreichbar (Typoskript 2.x) mit
Object.assign(target, source)
Beispiel:
Die Magie hier ist, dass
Object.assign<T, U>(t: T, u: U)
eingegeben wird, um die Kreuzung zurückzugebenT & U
.Die Durchsetzung, dass dies in eine bekannte Schnittstelle aufgelöst wird, ist ebenfalls unkompliziert. Zum Beispiel:
interface Foo { (a: number, b: string): string[]; foo: string; } let method: Foo = Object.assign( (a: number, b: string) => { return a * a; }, { foo: 10 } );
welche Fehler aufgrund inkompatibler Eingabe:
Nachteil: Sie müssen möglicherweise POLYfill Object.assign wenn älteren Browser - Targeting.
quelle
TypeScript wurde entwickelt, um diesen Fall durch Zusammenführen von Deklarationen zu behandeln :
Durch das Zusammenführen von Deklarationen können wir sagen, dass etwas sowohl eine Funktion als auch ein Namespace ist (internes Modul):
function f() { } namespace f { export var someValue = 3; }
Dadurch bleibt die Eingabe erhalten, und wir können sowohl
f()
als auch schreibenf.someValue
..d.ts
Verwenden Sie beim Schreiben einer Datei für vorhandenen JavaScript-Code Folgendesdeclare
:declare function f(): void; declare namespace f { export var someValue: number; }
Das Hinzufügen von Eigenschaften zu Funktionen ist in TypeScript häufig ein verwirrendes oder unerwartetes Muster. Versuchen Sie daher, es zu vermeiden. Dies kann jedoch erforderlich sein, wenn Sie älteren JS-Code verwenden oder konvertieren. Dies ist eines der wenigen Male, bei denen es angebracht wäre, interne Module (Namespaces) mit externen zu mischen.
quelle
function hello() { .. }
namespace hello { export const value = 5; }
export default hello;
IMO ausführen. Dies ist viel sauberer als Object.assign oder ähnliche Laufzeit-Hacks. Keine Laufzeit, keine Typzusicherungen, kein Nichts.Ich kann nicht sagen, dass es sehr einfach ist, aber es ist definitiv möglich:
Wenn Sie neugierig geworden sind, ist dies ein Kernstück von mir mit der TypeScript / JavaScript-Version von
Optional
quelle
Als Verknüpfung können Sie den Objektwert mithilfe des Accessors ['Eigenschaft'] dynamisch zuweisen:
var f = function() { } f['someValue'] = 3;
Dies umgeht die Typprüfung. Es ist jedoch ziemlich sicher, da Sie absichtlich auf die gleiche Weise auf die Immobilie zugreifen müssen:
var val = f.someValue; // This won't work var val = f['someValue']; // Yeah, I meant to do that
Wenn Sie jedoch wirklich möchten, dass der Typ den Eigenschaftswert überprüft, funktioniert dies nicht.
quelle
Eine aktualisierte Antwort: seit dem Hinzufügen von Kreuzungstypen über
&
ist es möglich, zwei abgeleitete Typen im laufenden Betrieb "zusammenzuführen".Hier ist ein allgemeiner Helfer, der die Eigenschaften eines Objekts liest
from
und über ein Objekt kopiertonto
. Es gibt dasselbe Objekt zurück,onto
jedoch mit einem neuen Typ, der beide Eigenschaftensätze enthält, sodass das Laufzeitverhalten korrekt beschrieben wird:function merge<T1, T2>(onto: T1, from: T2): T1 & T2 { Object.keys(from).forEach(key => onto[key] = from[key]); return onto as T1 & T2; }
Dieser Low-Level-Helfer führt zwar noch eine Typzusicherung durch, ist jedoch von Natur aus typsicher. Mit diesem Helfer haben wir einen Bediener, mit dem wir das OP-Problem mit voller Sicherheit lösen können:
interface Foo { (message: string): void; bar(count: number): void; } const foo: Foo = merge( (message: string) => console.log(`message is ${message}`), { bar(count: number) { console.log(`bar was passed ${count}`) } } );
Klicken Sie hier, um es auf dem TypeScript-Spielplatz auszuprobieren . Beachten Sie, dass wir uns darauf beschränkt haben
foo
, vom Typ zu seinFoo
, daher muss das Ergebnis vonmerge
vollständig seinFoo
. Also, wenn Sie umbenennenbar
inbad
Sie einen Tippfehler.Hinweis: Hier gibt es jedoch noch ein Loch. TypeScript bietet keine Möglichkeit, einen Typparameter auf "keine Funktion" zu beschränken. Sie könnten also verwirrt sein und Ihre Funktion als zweites Argument an übergeben
merge
, und das würde nicht funktionieren. Bis dies deklariert werden kann, müssen wir es zur Laufzeit abfangen:function merge<T1, T2>(onto: T1, from: T2): T1 & T2 { if (typeof from !== "object" || from instanceof Array) { throw new Error("merge: 'from' must be an ordinary object"); } Object.keys(from).forEach(key => onto[key] = from[key]); return onto as T1 & T2; }
quelle
Alte Frage, aber für Versionen von TypeScript ab 3.1 können Sie die Eigenschaftszuweisung einfach wie in einfachem JS durchführen, solange Sie eine Funktionsdeklaration oder das
const
Schlüsselwort für Ihre Variable verwenden:function f () {} f.someValue = 3; // fine const g = function () {}; g.someValue = 3; // also fine var h = function () {}; h.someValue = 3; // Error: "Property 'someValue' does not exist on type '() => void'"
Referenz und Online-Beispiel .
quelle
Dies weicht von starker Eingabe ab, aber Sie können dies tun
var f: any = function() { } f.someValue = 3;
Wenn Sie versuchen, drückend starkes Tippen zu umgehen, wie ich es war, als ich diese Frage fand. Leider ist dies ein Fall, in dem TypeScript bei einwandfreiem JavaScript fehlschlägt. Sie müssen TypeScript daher anweisen, sich zurückzuziehen.
"Ihr JavaScript ist vollkommen gültiges TypeScript" wird mit false bewertet. (Hinweis: mit 0,95)
quelle