Skip to content

RFC: @deriving for labeled arguments #6530

Open
@cometkim

Description

@cometkim

Motivation

JavaScript doesn't support labeled arguments, but ReScript does. So let add: (~a, ~b) in ReScript compiled to function add(a, b), so the labels a and b cannot be referenced by the call site.

In JS/TS codebase it is common to use object destructuring for this (e.g. props in React components) Also called RORO pattern

Until v10, gentype created a runtime mapper for this. However, since v11 it no longer generates runtime mappers. In the long term, gentype shouldn't have any runtime (See #6196)

However, the runtime mapper for props is an important feature for interoperability not only with TypeScript but also with JavaScript codebases in general. There must be an alternative to completely removing the feature.

This proposal solves the problem by extending the @deriving tag existing in the ReScript core.

Detailed Design

Basic

@genType
@deriving(params)
let add = (~a, ~b) => a + b

produces

type \"add$Params" = {
  a: int,
  b: int,
}

@genType
let \"add$with" = ({ a, b }: \"add$Params") => {
  let add = add(~a, ~b)
  add
}

This is only valid for functions with labeled arguments, and requires that the @genType tag be duplicated if it exists.

High-order functions

@deriving(params) can only be used on top-level functions. This is because new type bindings cannot be defined in a closure. Btw, user may want mappers for nested functions like:

@deriving(params)
let add = (~a, ~b) => {
  (~c, ~d) => {
    (e, f) => {
      a + b + c + d + e + f
    }
  }
}

If the return type of the function annotated by @deriving(params) is a function, then it should produces

type \"add$Params" = {
  a: int,
  b: int,
}

type \"add$Params$1" = {
  c: int,
  d: int,
}

@genType
let \"add$with" = ({ a, b }: \"add$Params") => {
  let add = add(~a, ~b)
  ({ c, d }: \"add$Params$1") => {
    let add = add(~c, ~b)
    add
  }
}

Deriving is applied in nested functions, but ends when it encounters a function with no labeled parameters.

Customizing definitions

User can customize derived typenames to make it more useful in TypeScript side.

@deriving({ params: "add(Props)" })
let add = (~a, ~b) => a + b
type \"Props" = {
  a: int,
  b: int,
}

let add = ({ a, b }: \"Props") => {
  let add = add(~a, ~b)
  add
}

Implementation

TBD

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions