Skip to content

Commit

Permalink
types improvements & new methods
Browse files Browse the repository at this point in the history
  • Loading branch information
infinite-system committed Aug 18, 2024
1 parent 7f902e3 commit 83d4d78
Show file tree
Hide file tree
Showing 10 changed files with 549 additions and 485 deletions.
2 changes: 1 addition & 1 deletion docs/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ export default defineConfig({
},
{ text: 'ExtractEmitTypes', link: '/pages/api#extractemittypes' },
{ text: 'ExtendSlots', link: '/pages/api#extendslots' },
{ text: 'UnwrapComposable', link: '/pages/api#unwrapcomposable' },
{ text: 'UseComposable', link: '/pages/api#UseComposable' },
],
},

Expand Down
7 changes: 3 additions & 4 deletions docs/docs/components/usage/CounterComposables.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ivue, UnwrapComposable } from 'ivue';
import { useMouse } from '@vueuse/core';
import { ivue, iref, iuse } from '../../../../src/index';
class Counter {
count = ref(0) as unknown as number;
count = iref(0);
increment() {
this.count++;
}
mouse = useMouse() as unknown as UnwrapComposable<typeof useMouse>
mouse = iuse(useMouse());
}
const counter = ivue(Counter);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script setup lang="ts">
import { ref } from 'vue';
import { ivue, type UnwrapComposable } from 'ivue';
import { useMouse } from './functions/useMouse';
import { ivue, type UseComposable } from '@/ivue';
/**
* Use the ivue Utility Type: UnwrapComposable<typeof YourComposableFunctionName>
* Use the ivue Utility Type: UseComposable<typeof YourComposableFunctionName>
* to get he resulting unwrapped composable properties and functions.
*/
type UseMouse = UnwrapComposable<typeof useMouse>;
type UseMouse = UseComposable<typeof useMouse>;
class Counter {
count = ref(0) as unknown as number;
Expand Down
10 changes: 5 additions & 5 deletions docs/docs/pages/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Core `ivue(className, ...args)` initializer is able to infer and validate the co

`ivue()` replicates native JavaScript / TypeScript class implementation by extending descriptors (getters and setters) up the whole prototype chain thus supporting classical inheritance.

Returns: `<IVue<T>>` or `ivue` `reactive()` object of an `AnyClass` class with flattened (de-Refed) `Refs` and `ComputedRefs` as properties.
**Returns:** `<IVue<T>>` or `ivue` `reactive()` object of an `AnyClass` class with flattened (de-Refed) `Refs` and `ComputedRefs` as properties.

### .init()

Expand All @@ -41,7 +41,7 @@ Returns: `<IVue<T>>` or `ivue` `reactive()` object of an `AnyClass` class with f
You can make `init()` method `async` if you need `await` functionality.
:::

Returns: `void | Promise<void>`
**Returns:** `void | Promise<void>`

### .toRefs()

Expand All @@ -60,7 +60,7 @@ const { width, height } = ivue(Box).toRefs(['width', 'height']);
This improves performance if `box` has many other properties that we do not need.
:::

Returns: `IVueRefs<InstanceType<T>>`
**Returns:** `IVueRefs<InstanceType<T>>`

## Utility Functions

Expand All @@ -77,7 +77,7 @@ export function propsWithDefaults<T extends VuePropsObject>(

Combines statically written defaults object with the runtime type definition of Vue 3 props.

Returns: `VuePropsWithDefaults<T>`
**Returns:** `VuePropsWithDefaults<T>`

## Utility Types

Expand All @@ -99,7 +99,7 @@ Extracts types of a runtime emits declaration.

Allows you to extend slots of a given slot interface.

### UnwrapComposable
### UseComposable

---

Expand Down
30 changes: 26 additions & 4 deletions docs/docs/pages/guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,32 @@
## Dos and Don'ts

```ts
type UseMouse = UseComposable<typeof useMouse>;
/**
* Example of a properly defined ivue class.
*/
class Counter {
/** ✅ Properly declared unwrapped composable. */
mouse: UseMouse;

constructor(public props: CounterProps, public emit: CounterEmit) {
this.mouse = useMouse() as unknown as UseMouse // ✅ Properly declared unwrapped composable.
}

/** ✅ Properly declared init function. */
init() {
/** ✅ Properly set lifecycle hook. */
onMounted(() => {
this.count = 4;
});

/** ✅ Properly set watch function */
watch(() => this.count, (newCount) => {
if (newCount === 5) {
alert('You reached the count of ' + newCount + '!');
}
})
}
/**
* Use ref() for the property and cast it to number
* because refs auto-unwrap inside reactive().
Expand Down Expand Up @@ -59,19 +81,20 @@ Next, we convert the types back to their normal types as if they have no reactiv
`ivue` recommends all class functions to be defined in plain full function style (not arrow functions), this allows all `ivue` classes to be extensible at any point. By using plain standard functions, it allows the developer to be able to override them at any time by simply extending the class.

### Do not use arrow functions in class declarations
:::warning

:::warning
Arrow functions break full extensibility of classes because they carry their own context at the point of declaration, so avoid using them inside of `ivue` classes.
:::

## constructor() vs .init()


#### Use `constructor()` to assign properties of the class and cast Refs to Unwrapped bare types. <br />

#### Use `.init()` to declare reactive state functions like `watch`, `watchEffect`, and lifecycle hooks like `onMounted`, `onBeforeMount` etc, do assignments of reactive properties, since `init()` already has access to `reactive()` state through `this`.<br />

<hr />

Inside the `constructor()` method you still have access to non-reactive state, because when `constructor()` is initialized, it does NOT yet have access to the reactive properties of the class, since it was not yet converted to `reactive()` by `ivue`, so if you use the properties like Refs or ComputedRefs inside `constructor()` you would have to use them with the `.value`.
Inside the `constructor()` method you still have access to non-reactive state, because when `constructor()` is initialized, it does NOT yet have access to the reactive properties of the class, since it was not yet converted to `reactive()` by `ivue`, so if you use the properties like Refs or ComputedRefs inside `constructor()` you would have to use them with the `.value`.

As a general rule, there is no need to manipulate the values in the `constructor()`, use constructor only for assigning the properties and casting the types of those assigned properties to the unwrapped (de-Refed) final state of the resulting `reactive()` object.

Expand Down Expand Up @@ -103,4 +126,3 @@ To match that unwrapping behavior, our class needs to Unwrap (or de-Ref) the typ
## Naming Conventions

To benefit from the full power of `ivue`, it is recommended to extract the classes into separate files. What has been an effective pattern is to name the classes and put them right beside components in the same folder that these classes are being used with. So if you have `CounterComponent.vue` component, it can have a class inside `CounterComponentClass.ts`, and you can store props, emits, and other runtime definitions inside `CounterComponentProps.ts`.

5 changes: 4 additions & 1 deletion docs/docs/pages/how-its-made.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ After many iterations where I created a whole inversion of control library for `

In that sweep of clarity, I got rid of 95% of the code that was just extra stuff and not the essence and left only `3` core functions: `ivue()`, `.init()`, `.toRefs()` only which are necessary to do everything `ivue` is set out to do.

Use Composition API composables inside `init()` function.

## Minimalism

Thus `ivue` has minimal surface area of the API making it very robust and easy to test.
By default `ivue` does not rely on decorators, though you can use decorators if you wish to.

## The Rest is Up To You
`ivue` is like a small mustard seed core for the big tree trunk of Class Based Reactive applications to be built around it, that's why the path of utter simplicity was chosen.
Everything else like an Inversion of Control (IOC) system, Traits, Mixins, Decorators can be built around the core `ivue` architecture and is upto the community of enthusiatic open source contributors, please share with us your vision of how you and all of can use `ivue` better.

Everythign else like an Inversion of Control (IoC) system, Traits, Mixins, Decorators can be built around the core `ivue` architecture and is upto the community of enthusiatic open source contributors.
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"build": "vitepress build docs",
"serve": "vitepress serve docs",
"preview": "vitepress build docs && vitepress serve docs",
"deploy": "node scripts/gh-pages-deploy.js"
"deploy": "node scripts/gh-pages-deploy.js",
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.10.0",
Expand Down
20 changes: 10 additions & 10 deletions src/__tests__/ivue.vitest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,20 +468,19 @@ describe('ivue', () => {
},
};

/** Params Defaults */
/** Example Params Defaults */
const defaults: ExtractPropDefaultTypes<typeof defaultTypes> = {
updatePath: '',
updateEntity: false, // { updateEntity: true } will enable updating of entity if { fetchEntity: 'entity_name' } is set; { updateEntity: 'another_entity' } can enable updating an entity with another fields template
updateFieldsTemplate: [],
updateFieldsParams: {
updatePath: '', // String
updateEntity: false, // Boolean
updateFieldsTemplate: [], // Array
updateFieldsParams: { // Object
active: true,
},
updateLabel: '',
/** Drag and Drop */
draggable: true,
updateLabel: 'Update Item', // String
draggable: true, // Boolean
/** Runtime Class Runner */
runner: SampleClass,
fn: () => {},
runner: SampleClass, // Class
fn: () => {}, // Function
};

const _propsWithDefaults = propsWithDefaults(defaults, defaultTypes);
Expand Down Expand Up @@ -512,6 +511,7 @@ describe('ivue', () => {
expect(isClass((_propsWithDefaults.runner.default as () => any)())).toBe(
true
);

// Function
expect(_propsWithDefaults.fn.default).toBeTypeOf('function');
expect(isClass((_propsWithDefaults.fn.default as () => any)())).toBe(
Expand Down
Loading

0 comments on commit 83d4d78

Please sign in to comment.