Skip to content

Commit ecd5acc

Browse files
committed
Merge branch 'hotfix/2.4.3'
2 parents 8d45fa1 + 406f238 commit ecd5acc

File tree

11 files changed

+135
-55
lines changed

11 files changed

+135
-55
lines changed

package-lock.json

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@studiometa/js-toolkit-workspace",
3-
"version": "2.4.2",
3+
"version": "2.4.3",
44
"private": true,
55
"workspaces": [
66
"packages/*"

packages/demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@studiometa/js-toolkit-demo",
3-
"version": "2.4.2",
3+
"version": "2.4.3",
44
"private": true,
55
"type": "commonjs",
66
"scripts": {

packages/docs/guide/going-further/typing-components.md

Lines changed: 88 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ To improve DX and autocompletion of a components' properties, it is possible to
88

99
```ts
1010
class Base<BaseInterface extends {
11-
$options: BaseOptions;
12-
$refs: BaseRefs;
13-
$children: BaseChildren;
11+
$el: HTMLElement;
12+
$options: Record<string, any>;
13+
$refs: Record<string, HTMLElement | HTMLElement[]>;
14+
$children: Record<string, Base | Promise<Base>>;
1415
}>
1516
```
1617

@@ -20,7 +21,9 @@ See below for an example of how to define the type parameter in JSDoc or in Type
2021
This guide assumes that you are familiar with [TypeScript types](https://www.typescriptlang.org/), make sure to read the basics before going any further.
2122
:::
2223

23-
## With JSDoc comments
24+
## Basic types
25+
26+
### With JSDoc comments
2427

2528
```js
2629
import { Base } from '@studiometa/js-toolkit';
@@ -30,7 +33,8 @@ import { Figure } from '@studiometa/ui';
3033
* @typedef {{ name: string, lazy: boolean }} ComponentOptions
3134
* @typedef {{ btn: HTMLButtonElement, items: HTMLElement[] }} ComponentRefs
3235
* @typedef {{ Figure: Figure, LazyComponent: Promise<LazyComponent> }} ComponentChildren
33-
* @extends {Base<{ $options: ComponentOptions, $refs: ComponentRefs, $children: ComponentChildren }>}
36+
* @typedef {{ $el: HTMLAnchorElement, $options: ComponentOptions, $refs: ComponentRefs, $children: ComponentChildren }} ComponentInterface
37+
* @extends {Base<ComponentInterface>}
3438
*/
3539
export default class Component extends Base {
3640
static config = {
@@ -47,6 +51,7 @@ export default class Component extends Base {
4751
};
4852

4953
mounted() {
54+
this.$el; // HTMLAnchorElement
5055
this.$refs.btn; // HTMLButtonElement
5156
this.$refs.items; // HTMLElement[]
5257
this.$options.name; // string
@@ -57,33 +62,30 @@ export default class Component extends Base {
5762
}
5863
```
5964

60-
## With TypeScript
65+
### With TypeScript
6166

6267
```ts
6368
import { Base } from '@studiometa/js-toolkit';
6469
import { Figure } from '@studiometa/ui';
6570
import type LazyComponent from './LazyComponent.js';
6671

67-
type ComponentOptions {
68-
name: string;
69-
lazy: boolean;
70-
}
71-
72-
type ComponentRefs {
73-
btn: HTMLButtonElement;
74-
items: HTMLElement[];
75-
}
76-
77-
type ComponentChildren {
78-
Figure: Figure;
79-
LazyComponent: Promise<LazyComponent>
80-
}
72+
type ComponentInterface = {
73+
$el: HTMLAnchorElement;
74+
$options: {
75+
name: string;
76+
lazy: boolean;
77+
};
78+
$refs: {
79+
btn: HTMLButtonElement;
80+
items: HTMLElement[];
81+
};
82+
$children: {
83+
Figure: Figure;
84+
LazyComponent: Promise<LazyComponent>;
85+
};
86+
};
8187

82-
export default class Component extends Base<{
83-
$options: ComponentOptions,
84-
$refs: ComponentRefs,
85-
$children: ComponentChildren,
86-
}> {
88+
export default class Component extends Base<ComponentInterface> {
8789
static config = {
8890
name: 'Component',
8991
refs: ['btn', 'items[]'],
@@ -98,6 +100,7 @@ export default class Component extends Base<{
98100
};
99101

100102
mounted() {
103+
this.$el; // HTMLAnchorElement
101104
this.$refs.btn; // HTMLButtonElement
102105
this.$refs.items; // HTMLElement[]
103106
this.$options.name; // string
@@ -107,3 +110,63 @@ export default class Component extends Base<{
107110
}
108111
}
109112
```
113+
114+
## Typing for extensibility
115+
116+
If you create a component that can be extended by other component, you will need to define a type parameter for it.
117+
118+
### With JSDoc comments
119+
120+
You will need to declare types in a separate comment from the class and import the `BaseTypeParameter` type from the `@studiometa/js-toolkit` package.
121+
122+
```js {2,10,11}
123+
/**
124+
* @typedef {import('@studiometa/js-toolkit').BaseTypeParameter} BaseTypeParameter
125+
* @typedef {{ name: string, lazy: boolean }} ComponentOptions
126+
* @typedef {{ btn: HTMLButtonElement, items: HTMLElement[] }} ComponentRefs
127+
* @typedef {{ Figure: Figure, LazyComponent: Promise<LazyComponent> }} ComponentChildren
128+
* @typedef {{ $el: HTMLAnchorElement, $options: ComponentOptions, $refs: ComponentRefs, $children: ComponentChildren }} ComponentInterface
129+
*/
130+
131+
/**
132+
* @template {BaseTypeParameter} [Interface={}]
133+
* @extends {Base<Interface & ComponentInterface>}
134+
*/
135+
export default class Component extends Base {
136+
// ...
137+
}
138+
```
139+
140+
You will then be able to use the `Component` class like the `Base` class:
141+
142+
```js
143+
import Component from './Component.js';
144+
145+
/**
146+
* @extends {Component<{ $el: HTMLButtonElement >}}
147+
*/
148+
export default class ChildComponent extends Component {
149+
// ...
150+
}
151+
```
152+
153+
### With TypeScript
154+
155+
```ts
156+
import { Base } from '@studiometa/js-toolkit';
157+
import type { BaseTypeParameter } from '@studiometa/js-toolkit';
158+
159+
export default class Component<Interface extends BaseTypeParameter = {}> extends Base<
160+
Interface & ComponentInterface
161+
> {
162+
// ...
163+
}
164+
```
165+
166+
```ts
167+
import Component from './Component.js';
168+
169+
export default class ChildComponent extends Component<{ $el: HTMLButtonElement }> {
170+
// ...
171+
}
172+
```

packages/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@studiometa/js-toolkit-docs",
3-
"version": "2.4.2",
3+
"version": "2.4.3",
44
"type": "module",
55
"private": true,
66
"scripts": {

packages/js-toolkit/Base/index.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function createAndTestManagers(instance) {
5555
/**
5656
* @typedef {typeof Base} BaseConstructor
5757
* @typedef {(Base) => Promise<BaseConstructor | { default: BaseConstructor }>} BaseAsyncConstructor
58-
* @typedef {OptionsManager & { [name:string]: any }} BaseOptions
58+
* @typedef {{ [name:string]: any }} BaseOptions
5959
* @typedef {{ [name:string]: HTMLElement | HTMLElement[] }} BaseRefs
6060
* @typedef {{ [nameOrSelector:string]: Base[] | Promise<Base>[] }} BaseChildren
6161
* @typedef {{ [nameOrSelector:string]: BaseConstructor | BaseAsyncConstructor }} BaseConfigComponents
@@ -74,6 +74,15 @@ function createAndTestManagers(instance) {
7474
* @property {BaseConfigOptions} [options]
7575
*/
7676

77+
/**
78+
* @typedef {{
79+
* $el?: HTMLElement,
80+
* $options?: BaseOptions,
81+
* $refs?: BaseRefs,
82+
* $children?: { [name:string]: Base | Promise<Base> },
83+
* }} BaseTypeParameter
84+
*/
85+
7786
/**
7887
* @typedef {Object} Managers
7988
* @property {typeof ChildrenManager} ChildrenManager
@@ -85,7 +94,7 @@ function createAndTestManagers(instance) {
8594

8695
/**
8796
* Base class.
88-
* @template {{ $options: BaseOptions, $refs: BaseRefs, $children: BaseChildren }} BaseInterface
97+
* @template {BaseTypeParameter} [BaseInterface={ $el: HTMLElement, $options: BaseOptions, $refs: BaseRefs, $children: { [name:string]: Base | Promise<Base> } }]
8998
*/
9099
export default class Base extends EventTarget {
91100
/**
@@ -109,7 +118,7 @@ export default class Base extends EventTarget {
109118

110119
/**
111120
* The root element.
112-
* @type {HTMLElement & { __base__?: WeakMap<BaseConstructor, Base | 'terminated'> }}
121+
* @type {BaseInterface['$el'] & HTMLElement & { __base__?: WeakMap<BaseConstructor, Base | 'terminated'> }}
113122
*/
114123
$el;
115124

@@ -217,39 +226,39 @@ export default class Base extends EventTarget {
217226
}
218227

219228
/**
220-
* @type {RefsManager & { [key in keyof BaseInterface['$refs']]: BaseInterface['$refs'][key] }}
229+
* @type {RefsManager & { [key in keyof BaseInterface['$refs']]: BaseInterface['$refs'][key] } & BaseRefs}
221230
* @private
222231
*/
223232
__refs;
224233

225234
/**
226-
* @returns {RefsManager & { [key in keyof BaseInterface['$refs']]: BaseInterface['$refs'][key] }}
235+
* @returns {RefsManager & { [key in keyof BaseInterface['$refs']]: BaseInterface['$refs'][key] } & BaseRefs}
227236
*/
228237
get $refs() {
229238
return this.__refs;
230239
}
231240

232241
/**
233-
* @type {BaseOptions & { [key in keyof BaseInterface['$options']]: BaseInterface['$options'][key] }}
242+
* @type {OptionsManager & { [key in keyof BaseInterface['$options']]: BaseInterface['$options'][key] } & BaseOptions}
234243
* @private
235244
*/
236245
__options;
237246

238247
/**
239-
* @returns {BaseOptions & { [key in keyof BaseInterface['$options']]: BaseInterface['$options'][key] }}
248+
* @returns {OptionsManager & { [key in keyof BaseInterface['$options']]: BaseInterface['$options'][key] } & BaseOptions}
240249
*/
241250
get $options() {
242251
return this.__options;
243252
}
244253

245254
/**
246-
* @type {ChildrenManager & { [key in keyof BaseInterface['$children']]: Array<BaseInterface['$children'][key]> }}
255+
* @type {ChildrenManager & { [key in keyof BaseInterface['$children']]: Array<BaseInterface['$children'][key]> } & BaseChildren}
247256
* @private
248257
*/
249258
__children;
250259

251260
/**
252-
* @returns {ChildrenManager & { [key in keyof BaseInterface['$children']]: Array<BaseInterface['$children'][key]> }}
261+
* @returns {ChildrenManager & { [key in keyof BaseInterface['$children']]: Array<BaseInterface['$children'][key]> } & BaseChildren}
253262
*/
254263
get $children() {
255264
return this.__children;

packages/js-toolkit/Base/managers/EventsManager.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ export default class EventsManager extends AbstractManager {
235235

236236
let index = 0;
237237
if (isArray(this.__base.$refs[refName])) {
238-
index = this.__base.$refs[refName].indexOf(ref);
238+
index = /** @type {HTMLElement[]} */ (this.__base.$refs[refName]).indexOf(ref);
239239
}
240240

241241
this.__base[method](event, index);
@@ -259,6 +259,7 @@ export default class EventsManager extends AbstractManager {
259259
.map((childName) => ({
260260
name: childName,
261261
child: childrenManager[childName].find(
262+
// @ts-ignore
262263
(instance) => instance === event.currentTarget || instance.$el === event.currentTarget
263264
),
264265
}))

packages/js-toolkit/helpers/getDirectChildren.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { isArray } from '../utils/index.js';
1111
* @param {Base} parentInstance
1212
* @param {string} parentName
1313
* @param {string} childrenName
14-
* @returns {Array<T>}
14+
* @returns {T[]}
1515
*/
1616
export function getDirectChildren(parentInstance, parentName, childrenName) {
1717
const children = parentInstance.$children[childrenName];
@@ -22,16 +22,19 @@ export function getDirectChildren(parentInstance, parentName, childrenName) {
2222
}
2323

2424
if (!isArray(nestedParents) || nestedParents.length <= 0) {
25-
return children;
25+
return /** @type {T[]} */ (children);
2626
}
2727

28-
return children.filter((child) => {
29-
return nestedParents.every((nestedParent) => {
30-
const nestedChildren = nestedParent.$children[childrenName];
31-
/* istanbul ignore next */
32-
return isArray(nestedChildren) ? !nestedChildren.includes(child) : true;
33-
});
34-
});
28+
return /** @type {T[]} */ (
29+
children.filter((child) => {
30+
return nestedParents.every((nestedParent) => {
31+
// @ts-ignore
32+
const nestedChildren = nestedParent.$children[childrenName];
33+
/* istanbul ignore next */
34+
return isArray(nestedChildren) ? !nestedChildren.includes(child) : true;
35+
});
36+
})
37+
);
3538
}
3639

3740
/**

packages/js-toolkit/index.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@ export { default as Base } from './Base/index.js';
22
export * from './decorators/index.js';
33
export * from './helpers/index.js';
44
export * from './services/index.js';
5+
6+
/**
7+
* @typedef {import('./Base/index.js').BaseTypeParameter} BaseTypeParameter
8+
*/

packages/js-toolkit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@studiometa/js-toolkit",
3-
"version": "2.4.2",
3+
"version": "2.4.3",
44
"description": "A set of useful little bits of JavaScript to boost your project! 🚀",
55
"publishConfig": {
66
"access": "public"

0 commit comments

Comments
 (0)