Skip to content

Comparison to JavaScript/TypeScript

Civet aims to be 99% compatible with existing JavaScript/TypeScript code. However, there are a few intentional deviations.

In the examples below, Civet code is on the lefttop, and compiled TypeScript output is on the rightbottom.

Single-Argument Arrow Functions

In Civet, arrow functions with a single argument need to wrap that argument in parentheses; otherwise, it gets treated like an implicit function call.

(x) => x + 1
x => x + 1
Edit inline or edit in the Playground!
(x) => x + 1;
x(() => x + 1);

The advantage of this approach is that zero-argument functions do not need () to indicate arguments:

=> console.log("Hello")
createEffect => console.log(signal())
Edit inline or edit in the Playground!
() => console.log("Hello");
createEffect(() => console.log(signal()));

Implicit Return

In Civet, all functions implicitly return the value of their last statement. You can disable this functionality by adding a semicolon at the end of the last statement, declaring a void return type, or adding an explicit return.

function more(x) {
  x+1
}
function hello() {
  console.log("Hello world!")
}
function hello() {
  console.log("Hello world!");
}
function hello(): void {
  console.log("Hello world!")
}
function hello() {
  console.log("Hello world!")
  return
}
Edit inline or edit in the Playground!
function more(x) {
  return x + 1;
}
function hello() {
  return console.log("Hello world!");
}
function hello() {
  console.log("Hello world!");
}
function hello(): void {
  console.log("Hello world!");
}
function hello() {
  console.log("Hello world!");
  return;
}

By contrast, in JavaScript, only arrow functions without braces implicitly return a value. This case works in Civet too; just be sure not to have a trailing semicolon which turns off implicit returns. And in Civet you get implicit returns from arrow functions with braces or indented blocks too.

(x) => x + 1
(x) => x + 1;
=> console.log("Hello world!")
=> console.log("Hello world!");
(x) => {
  if (x > 5)
    "large"
  else
    "small"
}
Edit inline or edit in the Playground!
(x) => x + 1;
(x) => {
  x + 1;
};
() => console.log("Hello world!");
() => {
  console.log("Hello world!");
};
(x) => {
  if (x > 5) {
    return "large";
  } else {
    return "small";
  }
};

If you don't like implicit returns outside of single-line arrow functions, you can turn off this feature entirely via a "civet" directive at the top of your file:

"civet -implicitReturns"
function hello() {
  console.log("Hello world!")
}
=>
  console.log("Hello world!")
(x) => x + 1
Edit inline or edit in the Playground!
function hello() {
  console.log("Hello world!");
}
() => {
  console.log("Hello world!");
};
(x) => x + 1;

Operator Spacing

Because Civet allows for implicit function calls without parentheses, symbol operators (+, -, etc.) need to be spaced consistently:

  • Unary operators (+, -, ~, !) cannot have spaces after them.
  • Binary operators (+, -, *, **, /, %, %%, ==, ===, <, >, <=, >=, <<, >>, >>>, &, &&, |, ||, ??) should either have spaces on both sides, or no space on either side. (Currently we also allow space after but not before the operator.)
  • Comma operator is forbidden in some contexts, e.g., array/object indexing. If you run into this limitation, wrap in parentheses.
  • Ternary operator x ?y :z needs space before both ? and : (in particular to distinguish from unary postfix x?).
  • Type parameters and arguments <T> cannot have spaces before them.
  • Regular expression literals cannot start with a space. (Use \ or [ ].)
x+y    // addition
x + y  // addition
x +y   // unary
//invalid: + y
Edit inline or edit in the Playground!
x + y; // addition
x + y; // addition
x(+y); // unary
//invalid: + y
x/y/z      // division
x / y / z  // division
x /y/ z    // regular expression
//invalid: x /y
Edit inline or edit in the Playground!
x / y / z; // division
x / y / z; // division
x(/y/(z)); // regular expression
//invalid: x /y
x<y>z      // comparison
x < y > z  // comparison
// JSX:
x <y> z
//invalid: x <y
Edit inline or edit in the Playground!
x < y && y > z; // comparison
x < y && y > z; // comparison
// JSX:
x(<y> z</y>);
//invalid: x <y

Comments

Civet supports ///.../// regular expression blocks. Thus, you should avoid comments that start with a triple slash — unless they are at the start of the file, as in TypeScript triple-slash directives.

/// A comment because at the top of file
if s
  /// not a comment
  ///.exec s
Edit inline or edit in the Playground!
/// A comment because at the top of file
if (s) {
  /notacomment/.exec(s);
}

Indentation

Because Civet allows for indented blocks as shorthand for braced blocks, it generally requires you to respect your own indentation.

(x) =>
  console.log("Hello")
  x+1
Edit inline or edit in the Playground!
(x) => {
  console.log("Hello");
  return x + 1;
};
if (condition)
  console.log("condition holds")
  console.log("condition still holds")
Edit inline or edit in the Playground!
if (condition) {
  console.log("condition holds");
  console.log("condition still holds");
}

By contrast, in JavaScript, the last indented line would have been treated as if it were at the top level.

Braced Blocks

Civet allows you to wrap your blocks in braces (or not), but to avoid conflicts with braced object literals, there are some restrictions. In particular, a one-line braced block must be on the same line as the if etc. it's part of:

// block
if (x) { y }
// not a block
if (x)
{ y }
Edit inline or edit in the Playground!
// block
if (x) {
  y;
}
// not a block
if (x) {
}
({ y });

Automatic Semicolon Insertion

JavaScript has complicated rules for automatic semicolon insertion. Civet has a different set of rules for what separates different statements, which are hopefully closer to your intuition. Binary operators and member access can continue on the next line, but only when spaced in the natural way (e.g. space after the binary operator).

x +
y
- z
-negative
[array]
.length
{name: value}
Edit inline or edit in the Playground!
x + y - z;
-negative;
[array].length;
({ name: value });

By contrast, JavaScript would treat the code on the lefttop as three binary operators with an index access (and a member access), followed by a code block with a label.

Keywords

Civet has some additional keywords beyond JavaScript's and TypeScript's, preventing them from being variable names. These include and, or, is, not, unless, and until. If you use these variables in your code, please rename them, e.g., by appending an underscore.

Labels

In Civet, labels are written :label instead of label:.

:label while (true) {
  break label
}
Edit inline or edit in the Playground!
label: while (true) {
  break label;
}

(This is to enable most object literals not needing braces.)

Decorators

Civet uses @ as shorthand for this, static, and constructor. Decorators need to be written with @@:

@@Object.seal
class Civet
  @name = "Civet"
Edit inline or edit in the Playground!
@Object.seal
class Civet {
  static name = "Civet";
}

Civet also does not support decorators on the same line as methods. This lets you use implicit function call syntax:

class Civet
  @@description translate "Caffeine time!"
  drink()
    @fetch @coffeeCup
Edit inline or edit in the Playground!
class Civet {
  @description(translate("Caffeine time!"))
  drink() {
    return this.fetch(this.coffeeCup);
  }
}

JSX

To allow for automatic closing of JSX tags, Civet requires JSX children to be properly indented.

<div>
  <h1>Hello</h1>
  <p>Text</p>
"not inside div anymore"
Edit inline or edit in the Playground!
<div>
  <h1>Hello</h1>
  <p>Text</p>
</div>;
("not inside div anymore");

If you're OK with explicitly closing all tags, you can turn off the indentation requirement via a "civet" directive:

"civet coffeeJSX"
<div>
  <h1>Hello</h1>
  <p>Text</p>
still inside div
</div>
Edit inline or edit in the Playground!
<div>
  <h1>Hello</h1>
  <p>Text</p>
  still inside div
</div>;

Another discrepancy is that Civet automatically combines consecutive JSX tags at the same indentation level into a JSX fragment. (Otherwise, only the last tag would serve a purpose.)

<h1>Hello</h1>
<p>Text</p>
Edit inline or edit in the Playground!
<>
  <h1>Hello</h1>
  <p>Text</p>
</>;

Sloppy Mode Features

Civet generally assumes (but does not enforce) that you are following JavaScript's strict mode, so it does not support:

  • with (consider using)
  • octal values of the form 0644 (use 0o644 instead)

Anything Else

If some existing JS/TS code does not parse in Civet and it does not fall into one of the categories above, it is likely a bug; please report it.


Comparison to CoffeeScript

If you are coming to Civet from a CoffeeScript background, check out this comparison.