Tartak is a functional programming language that compiles to TypeScript types.
My goal was to create a language that's not just a syntax sugar over TypeScript types, but a language that's as close to any general purpose programming language as possible.
Tartak uses hotscript, ts-arithmetic and many other tricks to make it happen.
🔔 Keep in mind that this project is a work in progress, may contain bugs and missing features. Also, see the ROADMAP section for planned features. Feel free to request features or report bugs with GitHub issues.
- first-class functions (closures, functions returning functions, partial application, etc.)
- support for arithmetic, relational and logical operators
- built-in test sections a la Rust
- pattern matching
// file: parseRoute.tartak
// exported types will be available for you to import from compiled files
// to distinguish between objects ({}) and blocks, blocks have a colon before the opening brace (:{})
export type parseRoute = (route: string) => :{
// assign computations to variables like this
let parts = route.split("/"); // -> ["users", "<id:string>", "posts", "<index:number>"]
// last expression is the return value, like in Rust
parts
// method chaining!
.filter((part) => part.startsWith("<")) // -> ["<id:string>", "<index:number>"]
.map((part) => match part {
// infer subparts of the expression during pattern matching
`<${infer name}:${infer ty}>` -> [name, ty]
}) // -> [["id", "string"], ["index", "number"]]
.toUnion() // -> ["id", "string"] | ["index", "number"]
.fromEntries() // -> { id: "string"; index: "number" }
.mapValues((ty) => match ty {
"string" -> string,
"number" -> number
}) // -> { id: string; index: number }
}
type params = parseRoute("/users/<id:string>/posts/<index:number>")
// ^?
// { id: string; index: number; }
// Add tests alongside your code
#[test] :{
AssertEqual(
parseRoute("/users/<id:string>/posts/<index:number>"),
{ id: string, index: number }
)
AssertEqual(
parseRoute("/users/noParams"),
{}
)
}
Then, compile the file with:
npx tartak parseRoute.tartak
This will generate a parseRoute.tartak.ts
file that exports parseRoute
type, which can be imported and used like this:
import { parseRoute } from "./routeParser.tartak";
declare function redirect<Route extends string>(
route: Route,
params: parseRoute<Route>
): void;
redirect("/api/v1/user/<id:string>", {
id: "123", // OK
});
redirect("/api/v1/user/<id:string>", {
id: 123, // Error: Type 'number' is not assignable to type 'string'
});
redirect("/api/v1/user/<id:string>", {
hello: "123", // Error: Object literal may only specify known properties, and hello does not exist in type:
});
First, install Tartak with your favorite package manager:
npm i -D tartak
yarn add -D tartak
pnpm add -D tartak
bun add -D tartak
Then, create a file with the .tartak
extension and start writing your code. You can place .tartak
files anywhere in your project.
For example:
// hello.tartak
export type greet = (name: string) => `hello ${name}`;
To compile your code into a TypeScript file, run:
# This will recursively look for all .tartak files starting from the current directory and compile them to corresponding *.tartak.ts files.
npx tartak
# Or, you can specify the path to the file you want to compile
npx tartak hello.tartak
# Or, you can watch for changes
npx tartak --watch
- readonly
- optional parameters
- language server?
- optional object properties
- cli: --watch flag
- imports/exports
- closures
- partial application
- arithmetic operators
- - (subtraction)
- - (negation)
-
+
- /
-
**
(power) - % (mod)
- relational and logical operators
- ==
- !=
- <
- <=
- >
-
>=
- && (logical and)
- || (logical or)
- ! (logical not)
- string methods
- .length
- .trimLeft
- .trimRight
- .trim
- .replace
- .slice
- .split
- .repeat
- .startsWith
- .endsWith
- .toTuple
- .toNumber
- .toString
- .stringsPrepend
- .stringsAppend
- .uppercase
- .lowercase
- .capitalize
- .uncapitalize
- .snakeCase
- .camelCase
- .kebabCase
- .compare
- .lessThan
- .lessThanOrEqual
- .greaterThan
- .greaterThanOrEqual
- number methods
- .abs
- object methods
- .readonly
- .mutable
- .required
- .partial
- .readonlyDeep
- .mutableDeep
- .requiredDeep
- .partialDeep
- .update
- .keys
- .values
- .allPath
- .get
- .fromEntries
- .entries
- .mapValues
- .mapKeys
- .assign
- .pick
- .pickBy
- .omit
- .omitBy
- .objectCamelCase
- .objectCamelCaseDeep
- .objectSnakeCase
- .objectSnakeCaseDeep
- .objectKebabCase
- .objectKebabCaseDeep
- union methods
- .mapUnion
- .extract
- .extractBy
- .exclude
- .excludeBy
- .unionNonNullable
- .unionToTuple
- .unionToIntersection
- tuple methods
- .partition
- .isEmpty
- .zip
- .zipWith
- .sort
- .head
- .tail
- .at
- .last
- .flatMap
- .find
- .drop
- .take
- .takeWhile
- .groupBy
- .join
- .map
- .filter
- .reduce
- .reduceRight
- .reverse
- .every
- .splitAt
- .toUnion
- .toIntersection
- .prepend
- .append
- .concat
- .min
- .max
- .sum
- objects
- accessing properties
- mapped types
- records
- unions
- intersections
- string literals
- match
- first-class testing framework? (kinda)
- syntax highlighting (barely works)
Currently one can symlink the tartak-syntax-highlighter
to the ~/.vscode/extensions
directory to get syntax highlighting in Visual Studio Code.
TODO: publish the extension to the marketplace.
- later TODO: create a language server.
this["arg0"]
is the captured environment of the function, parameters are stored in this["arg1"], this["arg2"], ...
.
Because hotscript supports up to 4 arguments, then tartak functions support up to 3 arguments.
This should be fine, because one of the argument can always be a tuple or an object.