-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[suggestion] type functions that can run type code within current scope (sort of like macros) #33818
Comments
This would require a ground-up rearchitecting of how binding and name resolution works, along with at least one additional full tree walk of every source file; I can't possibly imagine we would undertake that kind of effort unless this sort of thing was vastly more common and difficult to do. |
Ah, it may have been neat if it weren't so complicated. At the moment the boilerplate for each of my custom elements looks like this: import * as React from 'react'
import {Component} from './Component'
interface TimelineAttributes extends React.HTMLAttributes<Timeline> {
time?: number
}
class Timeline extends Component<TimelineAttributes>(HTMLElement) {
// ... skipped for brevity ...
}
customElements.define('time-line', Timeline)
declare global {
interface HTMLElementTagNameMap {
'time-line': Timeline
}
}
declare global {
namespace JSX {
interface IntrinsicElements {
'time-line': React.DetailedHTMLProps<TimelineAttributes, Timeline>
}
}
} I wonder if there is a better way. Suppose the previous example can be simplified to import * as React from 'react'
import {Component} from './Component'
interface TimelineAttributes extends React.HTMLAttributes<Timeline> {
time?: number
}
class Timeline extends Component<TimelineAttributes>(HTMLElement) {
// ... skipped for brevity ...
}
customElements.define('time-line', Timeline)
defineIntrinsicElement<'time-line', TimelineAttributes, Timeline> where Or maybe there's some other possibility than "type function". The main idea is to have some mechanism to make it possible to define types in bulk. The above idea is sort of like a "macro" but fully baked into the type system, intellisense, etc. |
The preprocessor idea in #4691 would help, but it adds much too many possibilities. What if TS allowed a limited type of preprocessor, call "type function" for example, so that something like typefunction defineIntrinsicElement<A, B, C> {
declare global {
namespace JSX {
interface IntrinsicElements {
${A}: React.DetailedHTMLProps<${B}, ${C}>
}
}
}
}
##defineIntrinsicElement<'my-component', MyComponentAttributes, MyComponent> would be the same as having written declare global {
namespace JSX {
interface IntrinsicElements {
'my-component': React.DetailedHTMLProps<MyComponentAttributes, MyComponent>
}
}
} It would have limited functionality, so people won't be inventing new language within TypeScript. For example, a requirement could be that "interpolations" would only be allowed in certain places so that arbitrary names maps to places where arbitrary names would be allowed to appear. |
This issue has been marked as "Too Complex" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Should this issue, be marked as I think a better name for this feature could be "type-only macros". The other macro feature requests, for example these,
are too complex indeed, because this wanted to have unlimited macros even for outputting JavaScript code, associating types and methods via new decorator-like macros, etc, etc. But the idea in this thread is limited to types only. Another syntax idea is: type macro defineElement<E, C, A> => {
declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements {
${E}: Pick<${C}, ${A}>
}
}
}
declare global {
interface HTMLElementTagNameMap {
${E}: ${C}
}
}
} It can only be declared in any location where a type can be declared, and it can only be expanded in locations where a type can be declared. The generic-like arguments ("type expression arguments"? I'm not sure the terminology) coud follow some rules like being limited to certain exporession types or locations, not arbitrary syntax. defineElement<"my-element", MyElementAttributes, MyElement> Additionally there'd be a rule where by it can be merged with a function, and can serve to combine a JavaScript function call with a type definition. The function call has to happen in a location where a statement could be placed, and would not be allowed in any expression-only location. type macro defineElement<E, C, A> =>
// ... same as before ...
function defineElement<E, C, A = 'foo' | 'bar'>(name: E, Class: C) {
customElements.define(name, Class)
}
// Use it:
defineElement('my-element', MyElement) The call to // The call still needs to happen, for plain JavaScript
defineElement('my-element', MyElement)
// But it would be as if this were written sibling to the function call:
declare module 'solid-js' {
namespace JSX {
interface IntrinsicElements {
'my-element': Pick<MyElement, 'foo' | 'bar'>
}
}
}
declare global {
interface HTMLElementTagNameMap {
'my-element': MyElement
}
} Then we could write 100 elements without repeating all that boilerplate. |
Search Terms
Suggestion
f.e., suppose I want to augment JSX types.
Examples
We can write
but imagine if we could write:
which would be a "type function" call that is essentially like a macro.
The implementation of
AddJSXType
could be something likeThis would prevent every Custom Element component from having to copy/paste all that boilerplate.
Is there currently another way?
Use Cases
opportunity to make some repetitive boilerplate-heavy things simpler/terser.
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: