⚡️ LightningScript
A vision for the future of TypeScript / Compile-to-JavaScript languages.
Why?#
JavaScript is a great but flawed language. For decades now, the JavaScript community has been trying to fix these flaws in many different ways.
On the one hand we have the standards process and tools like TypeScript that maintain backwards compatibility while adding new features. While this undoubtedly has its benefits, being unable to remove old features and syntax means that the language will always be held back by its past.
On the other hand, we have new languages like Elm, ReasonML, and ClojureScript that are able to make more radical changes to the language and its ecosystem. However, these languages can cause fragmentation as they are often not compatible with existing JavaScript code or require manual translation layers. The radical changes also mean that many JavaScript developers are unwilling to make the switch.
LightningScript is a vision for a language that combines the best of both worlds. A language that is instantly familiar to JavaScript developers, maintains the semantics of JavaScript, and is fully compatible with existing JavaScript code. At the same time, it is a language that is able to make changes to improve the developer experience and remove many of the flaws of JavaScript.
LightningScript is also a vision for a new ecosystem of tools and libraries that can work with or without using the new proposed syntax. The core goal of this vision is to establish a set of conventions for tools to reduce the amount of custom configuration required to build projects for the web.
What?#
NOTE: LightningScript is "Vision Document" and not a formal specification or implementation. Think of this as a proposal to the larger JavaScript community.
We want to start a convention and form consensus before building anything new and causing proliferation.
Goals#
- Maintain full interoperability with existing JavaScript code (like TypeScript)
- Maintain the semantics of JavaScript
- Be reasonably familiar to JavaScript developers
- Design for the existence of static types
New Features#
We propose adding a few new features to JavaScript. Most of these are already TC39 proposals, but we are choosing to make syntactic changes to make them more ergonomic without making it feel unfamiliar.
if
Expressions#
TC39 Proposal: Do expressions
If statements are useful but they are statements and not expressions. This means that they cannot be used in places where expressions are expected such as the RHS of an assignment or as a function argument.
We propose using if
as expressions and requiring the use of else
when it is used as an expression.
Additionally, if
expressions should be able to omit parenthesis around the condition, while
curly braces should always be required around the body.
if
statements should have optional parenthesis and required curly braces, as well.
Range Literals#
Introduce two new operators ..=
and ..<
to represent inclusive and exclusive ranges respectively.
These ranges are iterators that can be used in for..of
loops and can be used to create arrays using the spread operator.
They are not arrays themselves.
NOTE: The actual implementation could use a custom iterator that is more efficient than the generator function.
Array/String Slicing#
Range literals can be used to slice arrays and strings.
Pattern Matching#
TC39 Proposal: Pattern Matching
The TC39 proposal for pattern matching creates a new expression called match
and uses a new keyword when
to match
patterns.
When not encumbered with backwards compatibility, we propose using the switch
keyword to support pattern matching using
the familiar case
keyword. The let
keyword to "capture" values from the pattern, which is also part of the TC39 proposal.
Further to improve ergonomics, the parenthesis around the switch
expression there is no fallthrough by default so break
statements are not required.
Prior art: Swift.
This new switch
will also work as an expression where each "case" block implicitly returns the last expression.
is
Operator#
TC39 Proposal: Pattern Matching
The TC39 proposal for pattern matching includes a new is
operator to JavaScript. We propose using this operator
and removing the instanceof
and typeof
operators.
Pipeline Operator#
TC39 Proposal: Pipeline Operator
We propose adding a |>
Pipe operator to JavaScript. We propose using the Hack-style operator
which is the proposal that is currently winning proposal at TC39.
We are proposing adopting the TC39 proposal as-is and using the _
character as a placeholder for the LHS of the expression.
As a result of using _
as the placeholder, _
will no longer be a valid identifier within a pipeline expression.
When using the pipe operator, functions that take the placeholder value as the first argument should show up as auto-complete suggestions.
Rest Operator in Any Position#
Rest properties/parameters/elements should no longer limited to the final position.
Better JSX#
JSX has become a defacto standard for building user interfaces on the web. However, we can improve the ergonomics of JSX by making a couple of small changes:
- Require quotes around string literals as children
- Remove the need for
{}
around expressions as children - Allow prop name punning
This approach make JSX less like HTML and more like JavaScript.
Since {}
are no longer required around expressions, you should be able to use if
expressions and switch
expressions
Type System Features#
The following are features that would depend on a robust type system.
Tagged Enums#
TypeScript enums are have some idiosyncracies that make them less than ideal and they are not powerful enough to be used in place of tagged unions.
We propose expanding the capabilities of enums to make them more powerful.
These enums should support pattern matching using the switch
expression.
Also, when the enum type is know, it should be be possible to match on the
possible enum cases with a leading .
.
Typed Errors#
Prior art: Swift.
This change would require a big enhancement to the Type-System, but would result in more predictable and performant code.
This proposal involves three changes:
- Every function that can error should be marked as such.
try-catch
blocks should be replaced withdo-catch
blocks.- A
try
keyword must be required when calling a function that can error.
Let's consider these changes in more detail.
Consider a function such as JSON.parse
which can throw an error if the JSON is invalid.
The fact that it can throw should be part of its type signature.
When calling any function that can throw an error, the try
keyword must be used.
But any usage of try
itself must be within a do-catch
block or a function that can error.
Additionally, a convenience try?
keyword should be available to return undefined
instead of throwing an error.
Syntax changes#
Using from
first for Import Statements#
The from
keyword should be used first in import statements to improve auto-complete suggestions.
Import attributes should still be supported
Loops and Conditionals Require Curly Braces#
As mentioned above for if
and switch
expressions, all loops and conditionals should
require curly braces while parenthesis should be optional.
We also remove the do-while
and for-in
loops as they are rarely used
and can be replaced with while
and for-of
loops respectively.
Remove var
#
The var
keyword should be removed from the language. Only let
and const
should be used.
Replace the function
keyword with fn
#
The function
keyword should be replaced with fn
to reduce verbosity.
Fix ASI and Make Semi-Colons Optional#
LightningScript should reliably work without semi-colons.
JavaScript has ASI (Automatic Semicolon Insertion) which means that semi-colons are optional in many cases. However, there are pitfalls with ASI that can cause unexpected behavior.
Even when using semi-colons, there are still pitfalls with ASI:
Even though it looks like the function returns "Hello, world!"
, it actually returns undefined
.
To make semi-colons truly optional, we would need to remove these pitfalls by making the following changes to syntax:
- Disallow whitespace before the
(
when invoking a function - Disallow whitespace before
[
during Array or Object indexing. - Disallow unary
return
,yield
andthrow
statements.
The following patters would be disallowed:
However the following patterns would still be allowed:
As a result of these changes, the following patterns would be allowed, which are currently not allowed in JavaScript:
This syntax change should be unsurprising to JavaScript developers and make code even more intuitive.
Use is
instead of extends
for Generic Type Guards#
TypeScript uses the extends
keyword to define type guards. We propose using the is
keyword instead.
The extends
keyword should still be used for extending classes, but never for Types.
The is
keyword used within the return type for type-guards should remain unchanged.
Use if
expressions for conditional types#
TypeScript has a feature called "Conditional Types" which are used to create generic types that depend on a condition.
This feature uses a ternary operator to create the condition, but, being consistent with other changes we propose using if
expressions instead.
We can consider adding a switch
expression to TypeScript to make this feature even more powerful.
Use ?
symbol to mark optional types.#
Instead of having to write null | undefined | string
, you should be able to write string?
.
The only other usages of ?
in LightningScript would be for optional chaining (a?.b?.[0].?()
) and null coalescing (??
).
Remove Ternary Expressions#
Ternary expressions are famously hard to read and even with heroic efforts from Prettier, there seems to be no obvious way to format them that makes them readable for even a majority of developers.
Prettier Issue about Ternary Formatting
With support for if
expressions for both code and types, there is no need to keep ternary expressions
in the language.
We can remove ternary expressions from both code and conditional types.