Skip to content
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

Docs Reshuffle & Mini Brain Dump #10

Merged
merged 7 commits into from
Oct 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 41 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Cheatsheets for experienced Vue developers getting started with TypeScript.

- [Vue 3 specifics](vue-3.md)
- [Class Components & Decorators](class-components.md)

# Section 1: Setup

Expand Down Expand Up @@ -87,66 +88,6 @@ const Component = defineComponent({
});
```

### Class Components
[Vue Class Components](https://class-component.vuejs.org/) offers an alternative class-style syntax for Vue components which integrates well with TypeScript.

To have consistent support for decorators in your Vue components, it's also recommended to install [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator).


To get started with both libraries in your existing Vue project, run:
```
npm install vue-class-component vue-property-decorator
```

You only need to import `vue-property-decorator` into your `.vue` file as it extends `vue-class-component`.

You can now write TS in your components like this:

```vue
<template>
<div>
{{ count }}
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
{{ computedValue }}
</div>
</template>

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";

@Component
export default class Hello extends Vue {

count: number = 0
vue: string = "vue"
ts: string = "ts"

// Lifecycle methods can be accessed like this
mounted() {
console.log('component mounted')
}

// Method are component methods
increment(): void {
this.count++
}

decrement(): void {
this.count--
}

// Computed values are getters
get computedValue(): string {
return `${vue} and ${ts} rocks!`
}
}
</script>
```
See the [full guide for Vue Class Components](https://class-component.vuejs.org/guide/class-component.html#data).

> _Class components should not confused with the now abandoned [Class API proposal](https://github.com/vuejs/rfcs/pull/17#issuecomment-494242121)._

## Props

`PropType` can be used to annotate props with a particular object shape.
Expand Down Expand Up @@ -176,26 +117,34 @@ export default Vue.extend({
</script>
```

With vue-class-components and vue-property-decorator, you can use the `Prop` decorator:
Alternatively, you can also annote your prop types with an anonymous function:

```vue
import Vue from 'vue'

<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
import Vue from "vue";

interface PersonInfo {
firstName: string,
surname: string,
age: number
}

@Component
export default class InfoCard extends Vue {
@Prop({ required: true }) readonly info: PersonInfo;
}
export default Vue.extend({

name: "InfoCard",
props: {
info: {
type: Object as () => PersonInfo,
required: true
}
}
});
</script>
```

## Data Properties
## Data Properties (Options API)

You can enforce types on Vue data properties by annotating the return data object:

Expand Down Expand Up @@ -242,6 +191,31 @@ export default Vue.extend({
```
Note that [type assertion](https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions) like this does not provide any type safety. If for example, the `contents` property was missing in `newPost`, TypeScript would not catch this error.

## Computed Properties (Options API)

Typing the return type for your computed properties is important especially when `this` is involved as TypeScript sometimes has trouble infering the type.

```ts

export default Vue.extend({
data() {
return {
name: 'World',
}
},
computed: {
greet(): string { //👈 Remember to annotate your computed properties like so.
return 'Hello ' + this.name
},
}
})

```

>


# Other Vue + TypeScript resources
- Views on Vue podcast - https://devchat.tv/views-on-vue/vov-076-typescript-tell-all-with-jack-koppa/
- Focuses a lot on class components and vue-property-decorator - https://blog.logrocket.com/how-to-write-a-vue-js-app-completely-in-typescript/
- Vue 3 Hooks and Type Safety with TypeScript - https://www.youtube.com/watch?v=aJdi-uEKYAc
108 changes: 108 additions & 0 deletions class-components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Class Components

## Overview

[Vue Class Components](https://class-component.vuejs.org/) offers an alternative class-style syntax for Vue components which integrates well with TypeScript.

To have consistent support for decorators in your Vue components, it's also recommended to install [vue-property-decorator](https://github.com/kaorun343/vue-property-decorator).


To get started with both libraries in your existing Vue project, run:
```
npm install vue-class-component vue-property-decorator
```

You only need to import `vue-property-decorator` into your `.vue` file as it extends `vue-class-component`.

You can now write TS in your components like this:

```vue
<template>
<div>
{{ count }}
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
{{ computedValue }}
</div>
</template>

<script lang="ts">
import { Vue, Component } from "vue-property-decorator";

@Component
export default class Hello extends Vue {

count: number = 0
vue: string = "vue"
ts: string = "ts"

// Lifecycle methods can be accessed like this
mounted() {
console.log('component mounted')
}

// Method are component methods
increment(): void {
this.count++
}

decrement(): void {
this.count--
}

// Computed values are getters
get computedValue(): string {
return `${vue} and ${ts} rocks!`
}
}
</script>
```
See the [full guide for Vue Class Components](https://class-component.vuejs.org/guide/class-component.html#data).

> _Class components should not confused with the now abandoned [Class API proposal](https://github.com/vuejs/rfcs/pull/17#issuecomment-494242121)._

## Props
You can use the `Prop` decorator to annoate your prop types like so:

```ts
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";

interface PersonInfo {
firstName: string,
surname: string,
age: number
}

@Component
export default class InfoCard extends Vue {
@Prop() readonly info!: PersonInfo;
@Prop({ default: false }) readonly admin?: boolean;
}
</script>
```
Is equivalent to:

```ts
import Vue from "vue-property-decorator";
import Vue, { PropType } from 'vue'

interface PersonInfo {
firstName: string,
surname: string,
age: number
}
export default {
props: {
info: {
type: Object as PropType<PersonInfo>,
required: true
},
admin: {
type: Boolean,
default: false
}
},
}

```
43 changes: 43 additions & 0 deletions vue-3.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,46 @@ declare const props: {

export const welcome = computed(() => `Welcome, ${props.name}!`)
```

## Composition API

### Refs

Vue can infer the type of your `ref`'s but if you need to represent some more complex types you can do so with generics:

```ts
import {ref} from "vue"

interface PersonInfo {
firstName: string,
surname: string,
age: number
}

const people = ref<PersonInfo[]>([])

```

Alternatively you can use casting with `as`. This should be used if the type is unknown. Consider this example where we create a composition wrapper function around `fetch` and we dont know the data structure that will be returned.

```ts

import { ref, Ref } from "vue";

type ApiRequest = () => Promise<void>;

// When using this function we can supply the type via generics
export function useAPI<T>(url: RequestInfo, options?: RequestInit) {

const response = ref() as Ref<T>; // 👈 note we're typing our ref using `as`

const request: ApiRequest = async () => {
const resp = await fetch(url, options);
const data = await resp.json();
response.value = data;
};

return { response, request };
}

```