The Scenario
I am experimenting with the combination of Alpine.js and TypeScript. To achieve this, I am utilizing the community-maintained typings package @types/alpinejs (GitHub) along with the reusable components design pattern outlined here. Here's a basic example employing the AlpineComponent<T>
type:
// Type for Alphine components from Definitely Typed
import { AlpineComponent } from "alpinejs";
/**
* Declaration of the T-part of AlpineComponent<T> is necessary beforehand to avoid default type "any" for custom properties
*/
type Component = AlpineComponent<{
foo: string;
greet: (to: string) => string;
// Extensive list continues in real-world scenarios
}>
export default (): Component => {
return {
foo: "foo", // type "string" based on Component definition
bar: "bar", // inconveniently defaulted to type "any" due to lack of declaration within Component type...manual adjustment needed... 😩
greet(to) {
return `Hello ${to}!`;
},
/**
* init() invoked automatically by Alpine.js upon component mounting
*/
async init() {
console.log(this.greet("World")); // greet correctly returns type "string"
await this.$nextTick(); // special method defined in AlpineComponent type
},
}
}
In the example above, I define the type Component = AlpineComponent{...}
initially with all properties intended for use. Subsequently, I am required to retype them during component construction.
Challenges with Current Approach
- Repeating property typings as currently practiced can be cumbersome and convoluted.
- Upon alt-clicking any property, my IDE (VS Code) navigates to the definition in my custom type
Component
, rather than to the implementation within my codebase. Ideally, I seek insights into implementations over definitions.
The Question at Hand
Is there a more efficient way to streamline this process, minimizing redundant property typing? Perhaps an approach like inferring the object type returned from my component for the dynamic segment of AlpineComponent<T>
?