diff --git a/R/compile.R b/R/compile.R index 5c9e9d3..9d64aa7 100644 --- a/R/compile.R +++ b/R/compile.R @@ -8,10 +8,10 @@ ts_compile.ts_function <- function(f, name = deparse(substitute(f)), ...) { inputs <- attr(f, "args") result <- attr(f, "result") - inputs <- sapply(inputs, \(x) x$type) + inputs <- sapply(inputs, \(x) x$zod_type) fn_args <- paste(inputs) |> paste(collapse = ", ") - sprintf("const %s = R.ocap([%s], %s]);", name, fn_args, result$type_fn) + sprintf("const %s = R.ocap([%s], %s]);", name, fn_args, result$r_type) } # #' @export diff --git a/R/types.R b/R/types.R index 1e03ddd..e9daa2c 100644 --- a/R/types.R +++ b/R/types.R @@ -9,8 +9,8 @@ #' @param generic logical, if `TRUE` then the object is a generic type. #' #' @md -object <- function(type = "any", - type_fn = "any", +object <- function(zod_type = "any", + r_type = "any", default = NULL, check = function() stop("Not implemented"), generic = FALSE) { @@ -27,21 +27,21 @@ object <- function(type = "any", #' @export print.ts_object <- function(x, ...) { # name <- deparse(substitute(x)) - cat(sprintf("Input (ts) type: %s\n", x$type)) - cat(sprintf("Output (R) type: %s\n", x$type_fn)) + cat(sprintf("Zod type: %s\n", x$zod_type)) + cat(sprintf(" R type: %s\n", x$r_type)) } is_object <- function(x) { inherits(x, "ts_object") } -ts_union <- function(...) paste(..., sep = " | ") -ts_array <- function(type = c("number", "boolean", "string")) { - if (type == "number") { - return("Float64Array") +ts_union <- function(...) sprintf("z.union([%s])", paste(..., sep = ", ")) +ts_array <- function(type = c("z.number()", "z.boolean()", "z.string()")) { + if (type == "z.number()") { + return("z.instanceof(Float64Array)") } - if (type == "boolean") { - return("Uint8Array") + if (type == "z.boolean()") { + return("z.instanceof(Uint8Array)") } return("RTYPE.stringArray") } @@ -65,7 +65,7 @@ n_type_fun <- function(n, type) { #' @export ts_logical <- function(n = -1L) { object( - n_type(n, "boolean"), + n_type(n, "z.boolean()"), n_type_fun(n, "RTYPE.logical"), check = function(x) { if (!is.logical(x)) stop("Expected a boolean") @@ -78,7 +78,7 @@ ts_logical <- function(n = -1L) { #' @export ts_integer <- function(n = -1L) { object( - n_type(n, "number"), + n_type(n, "z.number()"), n_type_fun(n, "RTYPE.integer"), check = function(x) { if (!is.integer(x)) stop("Expected an integer") @@ -91,7 +91,7 @@ ts_integer <- function(n = -1L) { #' @export ts_numeric <- function(n = -1L) { object( - n_type(n, "number"), + n_type(n, "z.number()"), n_type_fun(n, "RTYPE.numeric"), check = function(x) { if (!is.numeric(x)) stop("Expected a number", call. = FALSE) @@ -106,7 +106,7 @@ ts_numeric <- function(n = -1L) { #' @export ts_character <- function(n = -1L) { object( - n_type(n, "string"), + n_type(n, "z.string()"), n_type_fun(n, "RTYPE.character"), check = function(x) { if (!is.character(x)) stop("Expected a string") diff --git a/README.Rmd b/README.Rmd index 9c5ae85..5626ab6 100644 --- a/README.Rmd +++ b/README.Rmd @@ -36,27 +36,24 @@ Writing functions is easy, just use the `ts_*()` functions to define formals and ```r library(ts) -app <- ts_app( +app <- ts_list( add = ts_fun( function(x, y) { x + y }, - x = ts_number(), - y = ts_number(), + x = ts_number(1), + y = ts_number(1), # ideally this will use a generic type where x OR y can be vectors # and, if one is a vector, the return type will be a vector too... - result = ts_number() + result = r_numeric(1) ), sample = ts_fun( function(x, n) { sample(x, n) }, - x = ts_character_vector(), - n = ts_integer(), - result = ts_condition(n, - 1 = ts_character(), - ts_character_vector() - ) + x = ts_string(), + n = ts_number(1), + result = r_character() ) ) @@ -73,7 +70,7 @@ export const app = { z.promise(R.numeric(1)) ), sample: z.function( - z.tuple([z.character_vector(), z.integer()]), + z.tuple([z.array(z.string()), z.integer()]), z.promise(R.character()) ) }; @@ -82,11 +79,8 @@ export const app = { which will generate the following types: ```typescript type App = { - add: (x: number, y: number) => Promise<{ data: number }>; - sample: (x: string[], n: number) => Promise<{ data: string | string[] }>; - // or, if possible, even better: - sample: (x: string[], n: N) => - Promise<{ data: N extends 1 ? string : string[] }>; + add: (x: number, y: number) => Promise>; + sample: (x: string[], n: number) => Promise; }; ``` diff --git a/README.md b/README.md index 9e2d8cd..9edaf84 100644 --- a/README.md +++ b/README.md @@ -28,27 +28,24 @@ formals and return types. ``` r library(ts) -app <- ts_app( +app <- ts_list( add = ts_fun( function(x, y) { x + y }, - x = ts_number(), - y = ts_number(), + x = ts_number(1), + y = ts_number(1), # ideally this will use a generic type where x OR y can be vectors # and, if one is a vector, the return type will be a vector too... - result = ts_number() + result = r_numeric(1) ), sample = ts_fun( function(x, n) { sample(x, n) }, - x = ts_character_vector(), - n = ts_integer(), - result = ts_condition(n, - 1 = ts_character(), - ts_character_vector() - ) + x = ts_string(), + n = ts_number(1), + result = r_character() ) ) @@ -66,7 +63,7 @@ export const app = { z.promise(R.numeric(1)) ), sample: z.function( - z.tuple([z.character_vector(), z.integer()]), + z.tuple([z.array(z.string()), z.integer()]), z.promise(R.character()) ) }; @@ -76,11 +73,8 @@ which will generate the following types: ``` typescript type App = { - add: (x: number, y: number) => Promise<{ data: number }>; - sample: (x: string[], n: number) => Promise<{ data: string | string[] }>; - // or, if possible, even better: - sample: (x: string[], n: N) => - Promise<{ data: N extends 1 ? string : string[] }>; + add: (x: number, y: number) => Promise>; + sample: (x: string[], n: number) => Promise; }; ``` @@ -113,7 +107,11 @@ cat(readLines("tests/testthat/app.R"), sep = "\n") #> ) ts_compile("tests/testthat/app.R", file = "") -#> Error in ts_compile.ts_function(e[[x]], file = file, name = x): unused argument (file = file) +#> import { stringArray, character, numeric } from 'rserve-ts'; +#> +#> const fn_first = R.ocap([z.union([z.string(), stringArray])], character(1)]); +#> const fn_mean = R.ocap([z.union([z.number(), z.instanceof(Float64Array)])], numeric(1)]); +#> const sample_num = R.ocap([z.instanceof(Float64Array)], numeric(1)]); ``` ## TODO diff --git a/man/object.Rd b/man/object.Rd index ce42aaf..816249b 100644 --- a/man/object.Rd +++ b/man/object.Rd @@ -5,23 +5,23 @@ \title{Typed object} \usage{ object( - type = "any", - type_fn = "any", + zod_type = "any", + r_type = "any", default = NULL, check = function() stop("Not implemented"), generic = FALSE ) } \arguments{ -\item{type}{The type of the object that Typescript expect to send to R.} - -\item{type_fn}{The type of the object that Typescript expects to recieve from R.} - \item{default}{The default value of the object.} \item{check}{A function that checks the object and returns it if it is valid. This operates on the R side and is mostly for development and debugging purposes. It is up to the developer to ensure that all functions return the correct type of object always.} \item{generic}{logical, if \code{TRUE} then the object is a generic type.} + +\item{type}{The type of the object that Typescript expect to send to R.} + +\item{type_fn}{The type of the object that Typescript expects to recieve from R.} } \description{ This is the base type for all typed objects. It is not meant to be used directly. diff --git a/tests/testthat/functions.R b/tests/testthat/functions.R index 27b38de..8fe3ae9 100644 --- a/tests/testthat/functions.R +++ b/tests/testthat/functions.R @@ -4,9 +4,24 @@ sample_num <- ts_function( x = ts_numeric(0), result = ts_numeric(1) ) -ts_compile(sample_num) + +sampler <- ts_function( + function() { + list( + sample_one = sample_num(0) + ) + }, + result = ts_list( + num = sample_num + ) +) + +ts_compile(d_normal) # compile to: -# const out = { -# sample_one: R.ocap([R.as_vector(z.number())], R.numeric(1)), -# }; +# const sampler = R.ocap( +# [], +# R.list({ +# num: R.ocap([], R.numeric(1)) +# }) +# );