Proposal: Comptime. Optimization hints for Preprocessors
See original GitHub issueSuggestion
🔍 Search Terms
compiler preprocessor inline comptime
✅ Viability Checklist
My suggestion meets these guidelines:
- This wouldn’t be a breaking change in existing TypeScript/JavaScript code
- This wouldn’t change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript’s Design Goals.
⭐ Suggestion
Add syntax hints that preprocessor can use. Typescript should add this so developers would have a common interface to tell there toolchains (in this context: typescript aware preprocessors) which functions or expression can be evaluate at compile time. Preprocessor could then evaluate the runtime code and make it run at compiletime and serve its response static.
This Suggestion is not the first idea proposed here, but it tries to fix the problem the other ones had.
🚫 Non Goals
Typescript should not evaluate the code itself. (Which #26534 suffered from) Typescript should not throw any comptime errors
📃 Motivating Example
This Idea is nothing new. Zig is one of the biggest languages which have implemented this feature.
And comptime is a great feature for the web, as code size matters (like for loading speeds). Yet we want readable code, which often suffers from more code. more readable code.
https://kristoff.it/blog/what-is-zig-comptime/ https://ziglang.org/documentation/master/#comptime
💻 Use Cases
1. comptime for setting a variable
New Valid Syntax:
const compiledAt = comptime new Date().getTime()
// or
const compiledAt = new Date().getTime() as comptime
Compiles directly to:
const compiledAt = new Date().getTime()
A Typescript aware preprocessor would turn this into:
const compiledAt = 1652206435966
2. comptime getting used in a function
New Valid Syntax:
comptime async function readConfigFile() {
const data = await Deno.readTextFile("config.json");
return JSON.parse(data);
}
console.log((await readConfigFile()).version)
Compiles directly to:
async function readConfigFile() {
const data = await Deno.readTextFile("config.json");
return JSON.parse(data);
}
console.log((await readConfigFile()).version)
A Typescript aware preprocessor would turn this into:
console.log("1.0.0-beta.1");
3. comptime validation depending on function paramets
New Valid Syntax:
// "comptime id" declares that this parameter needs to be known at compile time (aka preprocessor time)
function registerView(comptime id: string) {
comptime: {
if (!id.includes("-"))
throw new Error("Invalid ID");
if (id.toUpperCase() != id)
throw new Error("Invalid ID");
}
// Normal code goes here [ ... ]
}
registerView("Hey-100") // Valid for Typescript
registerView("HEY-100") // Valid for Typescript
Compiles directly to:
function registerView(id) {
comptime: { // A "Label" is a valid javascript syntax
if (!id.includes("-"))
throw new Error("Invalid ID");
if (id.toUpperCase() != id)
throw new Error("Invalid ID");
}
// Normal code goes here [ ... ]
}
registerView("Hey-100") // Valid for Javascript, Throws a runtime error (which could lead to a unknown bug)
registerView("HEY-100") // Valid for Javascript
A Typescript aware preprocessor would turn this into:
function registerView(id: string) {
// Normal code goes here [ ... ]
}
registerView("Hey-100") // Invalid for the Preprocessor, Throws a compiletime error (which would identify the bug directly)
registerView("HEY-100") // Valid for the Preprocessor
Issue Analytics
- State:
- Created a year ago
- Comments:6 (3 by maintainers)
This doesn’t really fit into our design parameters. There are a huge number of questions that would need to be answered here to make this work in the context of a type system, like what happens when you indirect one of these expressions, what the lexical environment is when these functions are evaluated, and so on.
If I had a program that really needed this, I’d write a pass-through
precompile<T>(x: T): T
function, then whatever build tool that’s evaluating these could look for calls to that function and do “the right thing”, whatever that is. You could even make functions that could only be precompiled by writing something likeThis makes the semantics of this extremely clear, which I would think is very important to avoid surprises since writing JS that looks like JS but does something non-JSy is bad.
But
import type
is not an expression-level syntax. Goal 8 states avoiding to add new expression-level syntax, so checking that point in the viability checklist is wrong.Shows that the concept works just fine. 😃
IMO adding a new keyword to the TypeScript language that third-party tools may use is the wrong approach and will just lead to confusion when users expect it to do something.