Grundsätzlich möchte ich die Interop mit TypeScript verbessern und um Hilfe und Anweisungen bitten.
Dies bedeutet eine Erweiterung des Fable-Compilers in zwei Bereichen:
Bitte zögern Sie nicht, mich im gesamten Beitrag zu korrigieren, alternative Implementierungen vorzuschlagen oder sogar darauf hinzuweisen, warum dies eine schlechte Idee ist. Jede Hilfe bezüglich Design, Implementierung oder anderem ist willkommen. Fühlen Sie sich frei, sich privat über Twitter oder Slack (oder Mail) zu verbinden.
Die verlinkten verwandten Probleme (siehe Ende dieses Beitrags) sollten einige sinnvolle Anwendungsfälle aufzeigen. Aber insbesondere:
import "./MyFile.fs"
in TypeScript/Javascript mit geeigneter IDE-Unterstützung zulassen. (Es ist nicht genau klar, was wir hier zusätzlich tun müssten, damit die IDE die Eingaben aufnimmt.)Ich möchte vorerst nur über 1 sprechen (und den Beitrag später erweitern)
Wenn ich mir die verfügbaren APIs und die damit verbundenen Diskussionen anschaue, denke ich, dass es am besten ist, die TypeScript-API zu verwenden, da es Online-Tools gibt, die einfach zu verstehen und damit zu arbeiten sind.
Ich habe ein bisschen mit der Codebasis gespielt und folgendes festgestellt:
fable-compiler-js
verwenden, wir brauchen nur ein paar Bindungen oder verwenden unsichere Aufrufe.Fable.AST
erstellt werden. Es sieht ähnlich aus wie Fable.AST
, aber nur typspezifisches Zeug (alles Ausdruckszeug wird entfernt).d.ts
-Dateien irgendwo ausgeben (neben der .fs
-Datei zum Beispiel).Ja, es ist wahrscheinlich ein bisschen Arbeit, aber das alles klingt für mich machbar. In der Praxis:
any
für alles andere verwendenopt-in
machen, bis wir sicherer sindIch werde diesen Abschnitt später oder während dieser Diskussion erweitern. Aber meine aktuellen Ideen sind:
type MyTypings = TscImport("typings.d.ts")
.fs
Dateien schreiben, zB basierend auf Attributen -> per Globbing zur Projektdatei hinzufügenDiese Ausgabe ist eine Fortsetzung von:
Neulich habe ich daran gearbeitet, dass ts2fable besser funktioniert, um Typen zu übersetzen. Aber ich hörte auf, nachdem ich anfing, Probleme mit der Notwendigkeit von typmarkierten Gewerkschaften zu haben. https://github.com/fsharp/fslang-suggestions/issues/538
Außerdem habe ich keine gute Möglichkeit gefunden, den Pick-Zeilenpolymorphismus mit dem F#-Spaltenpolymorphismus darzustellen. Für eine 1:1 Übersetzung von Typen passen sie nicht ganz zusammen.
Vielleicht könnten wir das Gegenteil tun und alle TypeScript-ASTs in F#-ASTs konvertieren. [böses, verrücktes Informatiker-Lachen einfügen]
Vielen Dank für diese ausführliche Ausgabe @matthid. Da wir über einige Typen gesprochen haben, bin ich zwar etwas skeptisch gegenüber der Typescript-Integration, da sich die Typsysteme beider Sprachen, wie @Luiz-Monad sagt, ziemlich stark unterschieden haben. Aber ich würde gerne widerlegt werden und sehen, wie wir .d.ts-Deklarationen nutzen können.
Ich kann sehen, dass Sie Ihre Hausaufgaben gemacht und die älteren Ausgaben rund um das Thema aufgelistet haben, damit ich die Informationen nicht wiederholen muss ... oder selbst danach suchen muss, weil ich sie bereits vergessen habe :) Aber ich werde es trotzdem tun versuchen Sie, einige Kommentare hinzuzufügen, wenn ich mich an Dinge erinnere. Vorerst zwei kurze Anmerkungen:
Danke!
Aber ich hörte auf, nachdem ich anfing, Probleme mit der Notwendigkeit von typmarkierten Gewerkschaften zu haben.
Außerdem habe ich keine gute Möglichkeit gefunden, den Pick-Zeilenpolymorphismus mit dem F#-Spaltenpolymorphismus darzustellen
Da wir über einige Typen gesprochen haben, bin ich zwar etwas skeptisch gegenüber der Typescript-Integration, da sich die Typsysteme beider Sprachen, wie @Luiz-Monad sagt, ziemlich stark unterschieden haben.
Ja, ich denke, das ist in der Tat ein Problem, aber ich denke derzeit, dass dies nur für "2" wirklich ein Problem ist. nicht für "1". (oder kennen Sie Beispiele, wo dies ein Problem für "1." ist?). Im Allgemeinen fühlt es sich an, als ob das Typsystem von TypeScript leistungsfähiger ist als das von F#. Dies ist höchstwahrscheinlich ein allgemeines Problem im Ökosystem, da TypeScript nur versucht, vorhandenes Javascript zu "tippen". Das bedeutet also im Grunde, dass wir Probleme haben, bestehenden Code unserem Typsystem zuzuordnen.
Um dies für "2" zu lösen. wir sind auch nicht verloren:
@ncave arbeitet bereits daran, die Anmerkungen in Babel AST #1615 zurückzubringen. Babel kann jetzt Typescript parsen, aber ich erinnere mich nicht, ob es umgekehrt funktioniert (Anmerkungen in Babel AST, die verwendet wurden, um Typescript auszugeben).
Ich habe etwas mit Google recherchiert, aber ich kann nicht herausfinden, was der Vorteil davon ist. Wenn babel keine .d.ts
Dateien daraus schreiben kann, wozu ist es gut? Vielleicht kannst du oder @ncave das erklären?
Beim Importieren von F#-Dateien in Typescript ist mir aufgefallen, dass Typescript Sie kein Nicht-js-Modul mit einem relativen Pfad deklarieren lässt. Es gibt jetzt einen Hinweis dazu in den Dokumenten.
Ja, ich denke, wir müssen ein bisschen herumfummeln, damit dieser Import einwandfrei funktioniert, aber im schlimmsten Fall können wir auch eine .ts
-Datei generieren, die die Deklaration und den richtigen .fs
-Import importiert, und dann können wir Leuten sagen, dass sie .ts
statt .fs
importieren sollen?
aber ich erinnere mich nicht, ob es umgekehrt funktioniert (Anmerkungen in Babel AST, die verwendet wurden, um Typescript auszugeben)
Wenn ich mich richtig erinnere, unterstützt Babel nur das Kompilieren von TypeScript in JavaScript. TypeScript wird nicht generiert.
Auf der ersten Fable Conf wurde zumindest erklärt, dass sie TypeScropt -> JavaScript-Transpilation unterstützen und wegen des ganzen Babel-Ökosystems, der Konfiguration und Optimierung einen "besseren Job" als der TypeScript-Compiler machen wollten.
Bei den ersten Versuchen, .d.ts-Dateien zu generieren, habe ich den babel-dts-generator verwendet, der in der Lage war, die Anmerkungen aus Babel AST zu extrahieren, um die Deklarationen zu erstellen. Aber es scheint, dass das Plugin jetzt nicht unterstützt wird :/ Eine andere Möglichkeit wäre, die Anmerkungen als JSDoc-Kommentare auszugeben, da Typescript sie auch verwenden kann , um Intellisense bereitzustellen .
Wenn ich mich nicht irre, bestand der Zweck von @ncave darin, Babel dazu zu bringen, die Typanmerkungen als Kommentare auszugeben, sie dann in einem weiteren Durchgang zu entfernen und schließlich zu sehen, ob das Ergebnis mit einer dieser Typescripts-Teilmengen kompatibel war, die auf WebAssembly wie AssemblyScript abzielen. Ja, schwarze Magie wie immer :wink: Aber wenn es in beiden Fällen darum geht, Typanmerkungen in die Ausgabe aufzunehmen, ist es eine Chance, zwei Fliegen mit einer Klappe zu schlagen.
Eine andere Möglichkeit wäre, die Annotationen als JSDoc-Kommentare auszugeben,
Ich habe diesen Vorschlag gesehen, aber ich bezweifle, dass es einfacher wäre, als TypeScript-Definitionen über die API zu schreiben. Auf der anderen Seite, wenn wir sie „kostenlos“ von babel bekommen, wäre das eine andere Geschichte und wahrscheinlich der einfachere Ansatz.
Die Frage ist, wie das zu Bibliotheken passt? Ich nehme an, wir würden vorschlagen, Kommentare in das Bündel aufzunehmen?
@alfonsogarciacaro
fable-library
zu etwas kompiliert wird, das der TypeScript-Compiler akzeptieren kann.Randnotiz:
Ich hoffe, dass sich das in Zukunft ändert, aber in der Zwischenzeit sollte das Targeting von TypeScript viel einfacher sein.
Der erste Schritt danach wäre sicherzustellen, dass die Fable-Library zu etwas kompiliert wird, das der TypeScript-Compiler akzeptieren kann.
Ich hoffe, dass sich das in Zukunft ändert, aber in der Zwischenzeit sollte das Targeting von TypeScript viel einfacher sein.
Nicht sicher, was diese bedeuten. Bedeutet dies, dass der Weg nach vorn darin besteht, die JSDoc-formatierte Ausgabe an den TypeScript-Compiler weiterzuleiten, der selbst die .d.ts
-Dateien schreibt? Oder meinst du das Schreiben .d.ts
Dateien von Fable selbst? Können Sie @ncave bitte klären?
Oder anders ausgedrückt: Ich bin bereit, etwas Zeit in dieses Problem zu investieren, aber nach all diesen Diskussionen bin ich mir immer noch nicht sicher, was wir zu diesem Zeitpunkt am besten können?
@mattid Das Hinzufügen von Typannotationen in der Babel-AST erzeugt Typen direkt in der Babel-Ausgabe. Es ist nur eine Frage der Vollständigkeit (geben Sie die richtigen Typanmerkungen für alle Eckfälle aus). Beispielsweise:
let rec factorial n =
if n = 0 then 1
else n * factorial (n-1)
let iterate action (array: 'T[]) =
for i = 0 to array.Length - 1 do
action array.[i]
let rec sum xs =
match xs with
| [] -> 0
| y::ys -> y + sum ys
kompiliert zu (mit aktivierten Typanmerkungen):
export function factorial(n: number): number {
if (n === 0) {
return 1;
} else {
return n * factorial(n - 1) | 0;
}
}
export function iterate<T>(action: (arg0: T) => void, array: Array<T>): void {
for (let i = 0; i <= array.length - 1; i++) {
action(array[i]);
}
}
export function sum(xs: any): number {
if (xs.tail != null) {
const ys = xs.tail;
const y = xs.head | 0;
return y + sum(ys) | 0;
} else {
return 0;
}
}
Wie Sie sehen können, ist es nicht perfekt und es gibt noch einiges zu tun (etliche Typen sind vorerst nur als any
gekürzt), aber meiner Meinung nach können Sie damit ziemlich weit kommen.
@ncave Und ich denke, TypeScript kann annotiertes JavaScript ootb verbrauchen? Oder müssen wir das weiterverarbeiten?
@matthid Das obige ist gültiges TypeScript, das von Babel produziert wurde, benennen Sie einfach .js
in .ts
.
Aber wie gesagt, die PR braucht etwas Liebe, um die richtigen Typen hinzuzufügen, es hat sich nicht wirklich viel geändert, seit @alfonsogarciacaro vor langer, langer Zeit in Fable 1 zum ersten Mal Typannotationen hinzugefügt hat.
Werde am Wochenende mal genauer hinschauen, danke für die Klarstellung
Ich werde bald einen next
-Zweig für die nächste Hauptversion erstellen, um #1839 zusammenzuführen. Wir könnten es auch verwenden, um #1615 zusammenzuführen und damit zu experimentieren :)
Ich wollte mich nur einschalten und sagen, dass ich sehr daran interessiert war, Fable bei meiner Arbeit einzuführen. Wir haben eine TypeScript-Codebasis von über 200.000 LOC, daher wäre es _riesig_, ein gewisses Maß an TypeScript-Integration in Fable zu haben.
Ich bin hauptsächlich daran interessiert, dass Fable TypeScript-Bindungen ausgibt, damit es einfacher ist, eine Fable-Bibliothek in unsere vorhandene TypeScript-Codebasis zu integrieren.
Ich habe an einigen Ergebnissen der jüngsten Arbeit von ncave herumgebastelt und versucht, einen Weg zu finden, TypeScrypt-Typen so zu erstellen, dass F#-Unions genau typisiert sind und mit einer switch-Anweisung unterschieden werden können, ohne die vorhandene Objektstruktur zu beschädigen. Ist dies der richtige Ort, um einige Ideen zu posten?
@chrisvanderpennen Sicher, warum nicht, wenn es verwandt ist. Oder Sie öffnen ein separates Diskussionsthema, wenn Sie möchten.
Verschoben nach #2096
@chrisvanderpennen Vielen Dank für die ausführliche Erklärung, es ist wahrscheinlich am besten, dies in ein eigenes Problem umzuwandeln [Funktionsanfrage], da es wahrscheinlich etwas über den anfänglichen Rahmen des einfachen Hinzufügens von Typen hinausgeht.
Sicher, ich werde eine erstellen und das obige bearbeiten, um darauf zu verweisen, damit diese Diskussion nicht überladen wird.
Bisher denke ich, dass die Idee ist, Javascript/Typoskript zu vermeiden, aber was ist, wenn Sie es annehmen können?
Ich habe kürzlich mit [Bolero] gearbeitet, was nur Webassembly ist, und die Art und Weise, wie es Javascript-Interop durchführt, besteht darin, Funktionsaufrufe auszuführen
https://github.com/AngelMunoz/Mandadin/blob/master/src/Mandadin.Client/Views/Notes.fs#L79
// next version of blazor will actually change
// to import the whole module (store the ref) then invoke the function
// instead of the actual Global Namespacing
Cmd.OfJS.either js "Namespace.MyFn" [||] Success Error
und auf der Javascript-Seite muss ich noch Code von Hand schreiben
https://github.com/AngelMunoz/Mandadin/tree/master/src/Mandadin.Client/wwwroot/js
und schließen Sie meine js-Bibliotheken ein, wenn dies groß genug wird, glaube ich, dass Sie diese Dateien und Abhängigkeiten sowieso bündeln müssten
Fable verwendet bereits Webpack und es sind letztendlich nur Javascript-Typoskript/Javascript-Dateien, zumindest denke ich das.
Die Eingaben/Abhängigkeiten sind nur eine "npm-Installation" entfernt.
// interop/my-file.ts
// to be included in the final fable bundle
import { libraryFn } from 'the-library-i-wont-write-bindings-to'
// hide the library interop/specifical JS work
function parseThingsAndWorkWithLibraries() { /* ... */ }
function imDoingStuff(someParam) {
// code
let someParam = someParam['something'] = myfn();
const parsed = parseThingsAndWorkWithLibraries(someParam)
return return { parsed }
}
// export only the F# interop bits
export async function myInteropFn(paramA: string, paramB: number): { my: string, fsharpType: boolean }} {
try {
const [unshapedResult, anotherResult] = await Promise.all([
libraryFn(paramA, paramB), imDoingStuff(paramA)
]);
return { my: unshapedResult.my_value, fsharpType: anotherResult.secondValue };
} catch(err) {
return Promise.reject(err.message);
}
}
und könnte in ähnlicher Weise wie diese konsumiert werden
// in the fable code somewhere
[<ImportMember("my-file")>]
let myInteropFn(params: string * number ): {| my: string; fsharpType: boolean |} = jsNative
Cmd.OfJS.either js myInteropFn ("value", 10) Success Error
Mein Denkprozess hier war, dass, wenn Fable eine Option für Interop in einer ähnlichen Angelegenheit implementieren könnte, die Komplexität von js-Interop an der F#/JS-Grenze liegen würde, nicht im Mangel an Händen, um in Werkzeuge/Bindungen zu investieren
Nun, dies wäre der "Worst Case", was bedeutet, dass Sie nur darauf zurückgreifen würden, wenn die Bibliothek wirklich groß genug für Sie selbst ist oder die Community keine Anstrengungen unternimmt, einige Bindungen zu schreiben. Die Sicherheit liegt immer auf der F#-Seite
Ich glaube, dass Sie die meiste Zeit keine externen Bibliotheken benötigen und die beliebten möglicherweise bereits abgedeckt sind
Wie auch immer ... das ist nur eine Idee, die ich hatte, als ich mit Bolero arbeitete. Ich glaube, Fable hat eine bessere Chance, dieses Interop-Modell zu verbessern als Blazor/Bolero, da die Pakete bereits in npm leben, es gibt zu wenige browserfähige Builds von Bibliotheken für Blazor/ Bolero zu arbeiten, am Ende glaube ich, dass sie immer noch auf die eine oder andere Weise auf die Bündelung zurückgreifen werden
Vielen Dank für deine Kommentare @AngelMunoz! Ich bin mir nicht sicher, ob ich das verstehe, es gibt bereits mehrere Möglichkeiten, mit JS-Code von Fable entweder typisiert oder nicht typisiert zu interagieren. Wonach suchen Sie speziell? https://fable.io/docs/communicate/js-from-fable.html
Mein Kommentar bezieht sich auf die Integration von Bibliotheken von Drittanbietern, die kompliziert zu automatisieren ist (ts2fable) und möglicherweise fehleranfällig ist. Außerdem besteht die einzige andere Alternative darin, Bindungen für Bibliotheken von Drittanbietern zu erstellen, und manchmal gibt es einfach nicht genug Hände, daher muss die Typoskript-Integration verbessert werden , oder das habe ich aus dem Problemthread verstanden.
Die Zusammenfassung wäre die folgende
Wenn Fable ein vom Benutzer erstelltes Javascript-/Typoskript-Bundle in derselben App enthält, sollten Sie die js aus den üblichen Mechanismen von Fable verwenden, ohne Bindungen für jede Bibliothek schreiben zu müssen, die Bindungen wären für Ihren spezifischen Code
Vielleicht ist dies ein anderes Problem und ich verstehe die Idee nicht
@alfonsogarciacaro
nach gestern meinte ich das
https://github.com/AngelMunoz/fable-plus-typescript-files-poc/blob/master/src/App.fs#L8
https://github.com/AngelMunoz/fable-plus-typescript-files-poc/blob/master/src/tsfiles/interop.ts#L18
das spricht Punkt 1 nicht an
Geben Sie TypeScript-Bindungen aus (wir schreiben sie derzeit von Hand)
aber es bietet weniger Reibung für Punkt 2 des Problems
TypeScript konsumieren/importieren (wir verwenden derzeit ts2fable dafür)
Es bringt natürlich seine eigenen Probleme mit sich, das erste, an das ich denken kann, ist die Sicherheit, das Typoskript-System kann wirklich gut sein, braucht aber Benutzerverstärkungen im Gegensatz zu F#, das standardmäßig sicherer ist
Bei Punkt 1 arbeitet @ncave daran, obwohl die Arbeit etwas auf Eis liegt, bis wir ein stabiles Fable 3 veröffentlichen. Bei Punkt 2 geht es darum, direkt Typoscript-Dateien auf typsichere Weise mit Bindungen (oder mit automatisch generierten Bindungen) zu konsumieren die Fliege). Ich denke, das ist kompliziert, könnte aber mit einem Typanbieter oder einem Tool zur Codegenerierung durchgeführt werden.
In jedem Fall ist es, wie oben kommentiert, bereits möglich, Typescript- oder JS-Dateien zu verwenden, indem dynamische Operatoren verwendet oder einige Ad-hoc-Bindungen geschrieben werden (das mache ich die ganze Zeit). Kein zusätzlicher Mechanismus erforderlich.
@alfonsogarciacaro : Wenn Sie sagen, dass Punkt 1 in Bearbeitung ist, bedeutet dies die Verwendung von "--typescript" in Fabel 3? Ist es das, wovon du sprichst?
Wenn ja, scheint dieses Flag echten Typoskript-Code zu erstellen (nicht nur Deklarationen).
Mein Ziel : eine Bibliothek in Fable erstellen, die ich in einer anderen Bibliothek verwenden möchte (die zweite in Typoskript). Bei Verwendung des Flags "--typescript" erhalte ich einige Fehler
import { decimal } from "./.fable/fable-library.3.0.1/Decimal.js"; // error : ... has no exported member decimal ...
Ich verstehe, dass es sich um eine laufende Arbeit handelt, aber könnten Sie einen Link zu der Arbeit hinzufügen?
Wo sollten wir Probleme bezüglich dieser spezifischen Funktion hinzufügen?
Was ist derzeit der richtige Weg, um eine Fable-Bibliothek in einer anderen Typoskript-Bibliothek zu verwenden?
@CedricDumont ja, das stimmt. Wobei ich gestehen muss, dass ich an diesem Feature nicht mitgearbeitet habe 😅 daher weiß ich nicht so recht, wie der aktuelle Stand ist. @ncave kann das wahrscheinlich besser beantworten. IIRC die Fable-Library-Importe geben Probleme, weil atm wir die Fable-Library-Dateien nicht in Typoskript kompilieren. Wir können versuchen, das zu lösen, aber ich bin mir nicht sicher, ob noch andere Probleme anstehen. Wenn wir diesem Feature einen Impuls geben wollen, sollten wir versuchen, die Tests beim Kompilieren nach Typescript laufen zu lassen.
Was ist derzeit der richtige Weg, um eine Fable-Bibliothek in einer anderen Typoskript-Bibliothek zu verwenden?
Im Moment schreiben Sie im Grunde genommen selbst die .d.ts-Deklaration für die Fable-Methoden, die Sie von TS verwenden. Ich habe festgestellt, dass die folgende Konfiguration funktioniert:
App.fs
App.fs.js # generated
App.d.ts # manually written
Wenn Ihre App.d.ts-Deklaration beispielsweise den folgenden Export enthält (entsprechend einem tatsächlichen Export in App.fs.js):
export function foo(x: number): {
data: string[]
};
Sie können es von Typescript wie folgt verwenden:
import { foo } from "./App"
const result = foo(5);
Beachten Sie, dass die Erweiterung beim Import weggelassen wird. Sie müssen Ihre webpack.config.js bearbeiten, damit Webpack in diesem Fall nach Dateien mit der Erweiterung .fs.js
sucht:
module.exports = {
resolve: {
extensions: ['.fs.js', '.mjs', '.js'], // Add other extensions you may want to use
},
...
}
Beachten Sie, dass dies auch nützlich ist, um Fable-Code zu verwenden, wenn Sie nur Javascript mit der // @ts-check
-Anweisung verwenden.
Hier ist der aktuelle Stand der TypeScript-Unterstützung, basierend auf diesem Kommentar :
Was den Fortschritt betrifft, so konnten wir in Fable 2 die Fable-Library zu striktem TypeScript kompilieren.
In Fable 3 hatten wir wegen der vielen Änderungen einen kleinen Rückschritt, aber es wird wieder eng.
Danach wäre das nächste große Ziel, alle Tests nach striktem TypeScript zu kompilieren, aber das hat einen viel größeren Umfang.
Wir müssen also zuerst einen kleinen Buckel überwinden und danach einen größeren, aber hoffentlich kann er nach dem ersten halbwegs benutzbar sein, wenn wir die TS-Version von fable-library
mit dem Fable-Compiler bündeln. Meinungen und Beiträge sind willkommen, Build-Schritte für TS fable-library
sind im oben genannten Kommentar beschrieben.
Schließung, da im Moment keine Arbeit in dieser Richtung stattfindet, bitte wieder öffnen, wenn jemand zur TS-Integration beitragen möchte.
Hilfreichster Kommentar
@matthid Das obige ist gültiges TypeScript, das von Babel produziert wurde, benennen Sie einfach
.js
in.ts
.Aber wie gesagt, die PR braucht etwas Liebe, um die richtigen Typen hinzuzufügen, es hat sich nicht wirklich viel geändert, seit @alfonsogarciacaro vor langer, langer Zeit in Fable 1 zum ersten Mal Typannotationen hinzugefügt hat.