-
Notifications
You must be signed in to change notification settings - Fork 476
TypeScript codegen backend #8118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
rescript
@rescript/darwin-arm64
@rescript/darwin-x64
@rescript/linux-arm64
@rescript/linux-x64
@rescript/runtime
@rescript/win32-x64
commit: |
This reverts commit 76f8a7a.
| readonly unsubscribe: () => void; | ||
| readonly closed: boolean; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
format indentation
| export type config = typeof $config; | ||
|
|
||
| export type api = typeof $api; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure this is really useful. they are value-only types.
|
Hi there, I played with this in rescript-kaplay and when adding the import type * as rescript from "@rescript/runtime/types";
import type * as Color-Kaplay from "@nojaf/rescript-kaplay/src/Components/Color.res.mjs";
import type * as Types-Kaplay from "@nojaf/rescript-kaplay/src/Types.res.mjs";
import type * as Vec2-Kaplay from "@nojaf/rescript-kaplay/src/Vec2.res.mjs";
export type t = rescript.opaque<"Wall-Skirmish.t", []>;
export interface rectOptions {
readonly radius?: rescript.option<number>;
readonly fill?: rescript.option<boolean>;
}
export interface areaCompOptions {
readonly shape?: rescript.option<Types-Kaplay.shape<Vec2-Kaplay.Local.t>>;
readonly offset?: rescript.option<Vec2-Kaplay.Local.t>;
readonly scale?: rescript.option<number>;
}in that project I do a lot of module inclusion stuff like: (sample) the generation there isn't quite right for the game objects. When I tried Just echoing some feedback here, keep it up! |
|
@nojaf thanks. I found two problems in the project you shared:
Both are trivial to fix. And I don't see any other significant issues with the generated type definitions. Let me fix them. |
|
I would make the typescript output as another |
|
@cometkim looks fantastic! Awesome work! I tried it on a few things, one was It surfaces a few issues that's probably easier to chase down by just trying it on that repo. Some examples: Namespaces seems to not be included in some referencesA namespace for module Actions: {
type t
@tag("kind")
type target = This | CssSelector({selector: string})
@tag("kind")
type rec action =
| ToggleClass({target: target, className: string})
| RemoveClass({target: target, className: string})
| AddClass({target: target, className: string})
| SwapClass({target: target, fromClassName: string, toClassName: string})
| RemoveElement({target: target})
| CopyToClipboard({
text: string,
onAfterSuccess?: array<action>,
onAfterFailure?: array<action>,
})
let make: array<action> => t
} = {
type t = string
@tag("kind")
type target = This | CssSelector({selector: string})
@tag("kind")
type rec action =
| ToggleClass({target: target, className: string})
| RemoveClass({target: target, className: string})
| AddClass({target: target, className: string})
| SwapClass({target: target, fromClassName: string, toClassName: string})
| RemoveElement({target: target})
| CopyToClipboard({
text: string,
onAfterSuccess?: array<action>,
onAfterFailure?: array<action>,
})
external stringifyActions: array<action> => string = "JSON.stringify"
let make = actions => stringifyActions(actions)
}Generates: declare namespace Actions {
type t = rescript.opaque<"ResX.Client.Actions.t", []>;
type target =
| "This"
| { readonly kind: "CssSelector"; readonly selector: string };
type action =
| {
readonly kind: "ToggleClass";
readonly target: Actions.target;
readonly className: string;
}
| {
readonly kind: "RemoveClass";
readonly target: Actions.target;
readonly className: string;
}
| {
readonly kind: "AddClass";
readonly target: Actions.target;
readonly className: string;
}
| {
readonly kind: "SwapClass";
readonly target: Actions.target;
readonly fromClassName: string;
readonly toClassName: string;
}
| { readonly kind: "RemoveElement"; readonly target: Actions.target }
| {
readonly kind: "CopyToClipboard";
readonly text: string;
readonly onAfterSuccess?: rescript.option<Actions.action[]>;
readonly onAfterFailure?: rescript.option<Actions.action[]>;
};
}
export type Actions = {
make: (arg0: Actions.action[]) => Actions.t;
};
// --NOTICE-- here how `actions` is not referenced from the namespace
function make(actions: action[]): string {
return JSON.stringify(actions);
}
let Actions: Actions = {
make: make
};Some packages tries to import from
|
|
The namespace issue got resolved, but the As mentioned in the docs. module Hero = {
type t = { name: string }
open Kaplay
// We want to draw an image on the screen for an object
include Sprite.Comp({type t = t})
// We want to change the position of the object
include Pos.Comp({type t = t})
}And a component looks like: module Comp = (
T: {
type t
},
) => {
@send
external numFrames: T.t => int = "numFrames"
@send
external play: (T.t, string) => unit = "play"
@get
external getSprite: T.t => string = "sprite"
@set
external setSprite: (T.t, string) => unit = "sprite"
// ...
}(sample) So I would expect the interface of
Right now I see: I understand this is probably quite the edge-case, but well, you asked 😉. |
|
That's expected one. |
|
Status update: d.ts is almost complete. However, the TypeScript output is getting messier. I don't expect this to add too much complexity. It should be possible to implement it with a reasonable level of complexity. Ideally, we could make it a single, unified process based on the new TS IR, with distinct printers. Omitting the type annotation produces the original JavaScript output, and omitting the implementation body produces the d.ts output. That's it. Right now, I'm avoiding modifying the existing code, which makes it feel very fragmented and unnecessarily complex. It's not only difficult to understand but also inefficient. I'm about to embark on a massive refactoring phase. This could create significant conflicts with new PRs, but I believe it's necessary. |
|
Tested against one of our company projects. It seems that type definitions for types that are private to a module (not exported in .resi) are not emitted to .ts, but those types are still referenced in function signatures etc. in the .ts output. |
|
In ReScript, I can do let map = Map.make()and the type of let schemas = new Map();which has type We would need to emit something like let schemas: Map<string, number> = new Map();(or whatever the concrete types are in the given case) to get rid of the |
|
Also saw that one:
|
See #6196 (comment) for context.
I tried to implement all the features we need, including those not supported by genType today.
Currently, it is somewhat complex to support both
.tsand.d.tsoutputs. Eventually, it could simplify the codegen process by unifying the codebase after deprecating genType and by organizing the context and config structures.Config
For dts:
{ "sources": "src", "package-specs": [ { "module": "esmodule", "in-source": true, + "dts": true } ] }For TypeScript
{ "sources": "src", "package-specs": [ { - "module": "esmodule", + "module": "typescript", "in-source": true - "dts": true } ] }Attributes
@as: Rename the generated types.@external("TypeName"): Use external (global) type@external(("package-name", "Name", true)): named type import@external(("package-name", "default", true)): default import as type@external(("package-name", "*", true)): namespace import as type@opaque: Force the type to be opaque