Repository navigation
Feature Request: Macros #4892
Copy link
Copy link
Closed as not planned
Labels
Out of ScopeThis idea sits outside of the TypeScript language design constraintsThis idea sits outside of the TypeScript language design constraintsSuggestionAn idea for TypeScriptAn idea for TypeScript
Description
This one might be waaaaaay out of scope, but I think it is worth proposing. The idea here is to add support for macros, functions that run at compile time, taking one or more AST nodes and returning an AST node or array of AST nodes. Examples/use cases: (Syntax off the top of my head, open to better ideas)
Interface-Based Validation
// validation.macros.ts
function interfaceToValidatorCreator(interface: ts.InterfaceDeclaration): ts.Statement {
// Would return something like "const [interface.name]Validator = new Validator({...});",
// using types from the interface
}
macro CreateValidator(interface: ts.InterfaceDeclaration) {
return [interface, interfaceToValidator(interface)];
}
// mainFile.ts
/// <macros path='./valididation.macros.ts' />
@#CreateValidator // Syntax 1: Decorator-Style
interface Person {
name: string;
age: number;
}
PersonValidator.validate(foo)Type Providers
// swagger.macros.ts
macro GetSwaggerClient(url: ts.StringLiteral): AssertionExpression {
// return something like "new SwaggerClient([url]) as SwaggerClientBase & {...}" where
// ... is an object creating the methods generated from the URL.
}
// mainFile.ts
/// <macros path='./swagger.macros.ts' />
var fooClient = #GetSwaggerClient("http://foo.com/swagger.json"); // Syntax 2: Function call syntax
fooClient.getPeople((people) => {
people.every((person) => console.log(person.firstName + "," + person.lastName);
});Conditional Compilation
// conditional-compilation.macros.ts
macro IfFlagSet(flagName: ts.StringLiteral, code: ts.Statement[]): ts.Statement[] {
return process.env[flagName.string] ? code : []
}
// mainFile.ts
/// <macros path='./compilation.macros.ts' />
#IfFlagSet("DEVELOPMENT") { // Syntax 3: Language Construct-Like (multiple arguments can be passed in parentheses)
expensiveUnnecessarySanityCheck()
}Notes
- Macros would run right after parsing. Not sure how we would deal with macros that need type information.
- This would make running
tscon unknown code as dangerous as running unknown code. It might be good to require a--unsafeAllowMacrosargument, not settable from atsconfig.json. - It might be worth nothing in the docs that the AST format may change at any time, or something along those likes
- The
macrokeyword would probably compile to a function, followed by ats.registerMacro(function, argumentTypes, returnTypecall. - Macros must be typed as returning a AST interface. This means that functions creating ASTs will probably need to have an explicit return type (or a calling function could have an explicit return type.
- Alternatively, we could consider giving the
kindproperty special treatment inmacros.tsfiles.
- Alternatively, we could consider giving the
- Just because a proposed syntax looks like a normal typescript construct doesn't mean it behaves like one.
#Foo(interface Bar{})is valid syntax, as long as there is a macro namedFoothat takes an interface.- Exception: The Decorator syntax might need to be a bit more choosy (no decorating
1 + 1, but decorating Interfaces, interface items, functions, etc. should be fine.
- Exception: The Decorator syntax might need to be a bit more choosy (no decorating
- This issue is likely to be updated quite a bit. For a log of changes, see the gist
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Out of ScopeThis idea sits outside of the TypeScript language design constraintsThis idea sits outside of the TypeScript language design constraintsSuggestionAn idea for TypeScriptAn idea for TypeScript
Activity
mhegazy commentedon Sep 21, 2015
duplicate of #3136?
Gaelan commentedon Sep 21, 2015
Mohamed Hegazy (@mhegazy) I don't think so. Support for macros as AST->AST functions would let us do anything you could do with a type provider (just return an
AssertionExpression, see the second example in the issue text), but also conditional compilation (return the passed code if the condition is true, otherwise do nothing), as well as general boilerplate reduction.arkdemon commentedon Nov 28, 2015
+1
fgalassi commentedon Dec 14, 2015
+1
elbarzil commentedon Dec 23, 2015
+1 (I almost expected it to work with sweet.js.)
ulfryk commentedon Dec 29, 2015
+1
nisimjoseph commentedon Jan 9, 2016
+1
joergwasmeier commentedon Jan 10, 2016
You can also look at the haxe macro system how they implemented it. http://haxe.org/manual/macro.html
soycabanillas commentedon Jan 14, 2016
+1
Zorgatone commentedon Jan 22, 2016
+1 👍
carywreams commentedon Feb 10, 2016
Question
Could this be provided by a pre-compile hook; between slurping the .ts file and depositing the .js file?
Possible Benefits
Of course, I could be missing some fundamental concept of macros and compilers, rather than just attempting to break up the traditional view.
lazdmx commentedon Feb 25, 2016
+1
gvkhna commentedon Apr 12, 2016
+1
80 remaining items
hazae41 commentedon Oct 20, 2023
Bun "macros" are not functions that can deeply modify the code but just regular JS functions evaluated at compile-time (they must output JS values)
What most people need is a function that can be called anywhere, and that will be output some arbitrary code (classes, functions, React components, code blocks)
For example, instead of creating a high-order-class, a macro would generate all the classes you need
hazae41 commentedon Oct 23, 2023
I have built something that can solve many use cases, without using AST
https://github.com/hazae41/saumon
Macros are like normal functions, but the preprocessor will replace their call by the string value they return
RyanCavanaugh commentedon Oct 23, 2023
This comment is correct; macros are definitely not in scope with our current definition of what we would/would not do in TypeScript.
alshdavid commentedon Nov 1, 2023
Is this something that could be implemented by the community as an extension to tsc and the TypeScript language server?
More generally, does the TypeScript compiler and TypeScript language server have the ability to be "exteneded" to include custom syntax - like the inclusion of compile time macros?
RyanCavanaugh commentedon Nov 2, 2023
No
owl-from-hogvarts commentedon Nov 3, 2023
Why no? What about typescript tranformers? ttypescript as example
nonara commentedon Nov 3, 2023
owl from hogvarts (@owl-from-hogvarts)
An extension like what's described here would require extending the compiler codebase itself. ttypescript and ts-patch allow you to add transformers to the compilation process, but they don't allow you to transform the compiler — not yet, anyway.
RyanCavanaugh commentedon Nov 3, 2023
I was asked a factual question and answered it 🤷♂️
owl-from-hogvarts commentedon Nov 3, 2023
So then sorry. It seems I have misunderstood the question)
Anyway, my though is that macroses potentially can be implemented via transformers api 🤔
alshdavid commentedon Sep 13, 2024
One thought I had was to implement Rust style macros as an swc plugin expressed as magic comments. Consider the following examples:
Another case is proc macros (not a real case but as an example)
Both can be described with magic-comment style notation however the content within the macro function parameters would be considered normal JavaScript and custom syntax (like jsx) would break type checking and the procedural macro would not have types
Without type checking, the feature is DOA. This is sad because it's a very valuable and flexible way to expand the language without putting the burden on the TypeScript team to maintain
e.g. React JSX should be maintained by the React team, styled components could be a macro, Angular decorators could probably be a custom procedural macro and frameworks like Svelte and Vue that use custom file extensions would have a better alternative for expressing their templating syntax
After working in Rust for the last 2 years, I am craving macros in TypeScript. We already have a pseudo macro - jsx. So close yet so far haha
alshdavid commentedon Dec 1, 2024
I'm sorry for reopening this but I really feel it would make a big difference to how TypeScript is used
#60645
ariccio commentedon Dec 1, 2024
I mean, let's be real, as you mentioned above, if even rust has macros it's goofy that typescript doesn't. I might expect it to be a reasonable fight-on-this-hill for a high end very high level proprietary functional language like the Wolfram/mathematica language, but typescript isn't at that extreme level of first class metaprogramming just yet