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
(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())
() => 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
}
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"
}
(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
function hello() {
  console.log("Hello world!");
}
() => {
  console.log("Hello world!");
};
(x) => x + 1;

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
(x) => {
  console.log("Hello");
  return x + 1;
};
if (condition)
  console.log("condition holds")
  console.log("condition still holds")
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.

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}
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.

Labels

In Civet, labels are written :label instead of label:. (The one exception is $: which can be written as $: for Svelte compatibility.)

:label while (true) {
  break label
}
$: document.title = title
label: while (true) {
  break label;
}
$: document.title = title;

(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"
@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
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"
<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>
<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 function.)

<h1>Hello</h1>
<p>Text</p>
<>
  <h1>Hello</h1>
  <p>Text</p>
</>;

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.

Civet v0.6.6