Skip to content

Commit

Permalink
fix(types): use AnyAbility in ComponentOptions and in Vue augmentat…
Browse files Browse the repository at this point in the history
…ion modules

Utilizes Vue type inference for functional Can component. So, `Can` component has an ExtendedVue type now
  • Loading branch information
stalniy committed Apr 22, 2020
1 parent c9778dd commit 7f9be6f
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 19 deletions.
45 changes: 34 additions & 11 deletions docs-src/src/content/pages/package/casl-vue/en.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ It accepts default slot and 5 properties:
* `field` - checked field

```html
<Can I="read" :this=post field="title">
<Can I="read" :this="post" field="title">
Yes, you can do this! ;)
</Can>
```
Expand Down Expand Up @@ -157,21 +157,44 @@ There are several other property aliases which allow constructing a readable que

The package is written in TypeScript, so don't worry that you need to keep all the properties and aliases in mind. If you use TypeScript, your IDE will suggest you the correct usage and TypeScript will warn you if you make a mistake.

To customize `Ability` generic parameters, you to redeclare `Vue.$ability` property:
To customize `Ability` generic parameters, you need to redeclare `Vue.$ability` property but TypeScript doesn't allow for property to have a different type. That's why the only option is to use application specific vue type:

```ts @{data-filename="appAbility.d.ts"}
import { AppAbility } from './services/ability';
```ts @{data-filename="AppVue.ts"}
import Vue, { VueConstructor } from 'vue';
import { AppAbility } from './services/AppAbility';

declare module 'vue/types/vue' {
interface Vue {
$ability: AppAbility
}
export interface AppVue extends Vue {
$ability: AppAbility
}

export default Vue as VueConstructor<AppVue>;
export * from 'vue';
```

and in `./services/ability.ts`:
and use `AppVue` to create components, for example:

```html
<template>
<div>hello world</div>
</template>

<script lang="ts">
import AppVue from '../AppVue';
export default Vue.extend({
name: 'HelloWorld'
props: {
name: String
}
});
</script>
```

Only in this case, you will get proper hints for `$can` method. Otherwise TypeScript infers types from `AnyAbility` for `$can` method which are not quite useful (i.e., `[any] | [any, any, string?]`)

So, after fixing vue types we can get back to `AppAbility` in `./services/AppAbility.ts`:

```ts @{data-filename="ability.ts"}
```ts @{data-filename="AppAbility.ts"}
import { Ability } from '@casl/angular';

type Actions = 'create' | 'read' | 'update' | 'delete';
Expand All @@ -180,7 +203,7 @@ type Subjects = 'Article' | 'User'
export type AppAbility = Ability<[Actions, Subjects]>;
```

> Read [Vue Typescript](https://vuejs.org/v2/guide/typescript.html) to understand why you need to redeclare `vue/types/vue` module.
> Read [Vue Typescript](https://vuejs.org/v2/guide/typescript.html) to get more information.
## Update Ability instance

Expand Down
12 changes: 5 additions & 7 deletions packages/casl-vue/src/component/can.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionalComponentOptions, VNode } from 'vue';
import Vue, { VNode } from 'vue';
import { SubjectType, Generics, AnyAbility, Abilities, IfString, AbilityTuple } from '@casl/ability';
import { VueAbility } from '../types';

Expand All @@ -17,7 +17,7 @@ export type AllCanProps<T extends AnyAbility> = AbilityCanProps<Generics<T>['abi
passThrough?: boolean
};

const Can: FunctionalComponentOptions<AllCanProps<VueAbility>> = {
export default Vue.extend<AllCanProps<VueAbility>>({
name: 'Can',
functional: true,
props: {
Expand All @@ -44,7 +44,7 @@ const Can: FunctionalComponentOptions<AllCanProps<VueAbility>> = {
const canRender = props.not ? !isAllowed : isAllowed;

if (!props.passThrough) {
return canRender ? children : (null as unknown as VNode);
return canRender ? children : [];
}

if (!data.scopedSlots || !data.scopedSlots.default) {
Expand All @@ -54,8 +54,6 @@ const Can: FunctionalComponentOptions<AllCanProps<VueAbility>> = {
return data.scopedSlots.default({
allowed: canRender,
ability: parent.$ability,
}) as unknown as VNode[];
}) as VNode;
}
};

export default Can;
});
2 changes: 1 addition & 1 deletion packages/casl-vue/src/extendVueTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ declare module 'vue/types/vue' {

declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
ability?: V['$ability'];
ability?: AnyAbility;
}
}

0 comments on commit 7f9be6f

Please sign in to comment.