Why Typescript
Typescript is an open-source language which builds on Javascript by adding static type definitions.
Javascript by default is what is typically called a dynamically type language. Which means that the variables can be assigned any type of value
e.g.
var mysteryBox = "Mystery!" || [] || 6
Typescript can be added incrementally to a project.
How to Setup Typescript
npm install -g @ vue/cli
# OR
yarn global add @vue/cli
Normale Installation von Vue3 mit Typescript durchführen
class-style-syntax NO
Babel YES
tsconfig.json
- paths
- "@/*" ⇒ Beschreibt wo Typescript nachschauen soll wenn eine Zeile mit "@" beginnt
- include
- Zeigt TypeScript welche Dateien benötigt werden um die Datei zu kompilen
Adding Typescript to an existing Vue 3 Project
vue add typescript
# class-style-syntax NO
# Babel YES
# Convert all .js to .ts YES
# Allow .js files to be compiled: NO
# Skip type checking of all declaration files YES
Adding Typescript to an existing Component
<script lang="ts">
Füge zum Script-Tag das Attribut lang
mit dem Wert ts
hinzu
import { defineComponent } from 'vue'
Import the defineComponent
onto the top to the functional part of your Component
export default defineComponent ({
...
})
// Wird zur Funktion! Runde Klammern nicht vergessen!!!
füge defineComponent
zum Modul hinzu
Common JavaScript-Types
- String
- Number
- Boolean
- Array
- Function
- Object
Additional Types provided from TypeScript
Any – deaktiviert das type checking
Tupel – vordefinierte Länge eines Arrays mit vordefinierten Datentypen
- Beispiel
// Example: RGB colors in an array [number, number, number]
Enum – Aufzählungstypen deren Aufzählung optional Werte zugewiesen werden können.
- Beispiel
enum ArrowKeys { Up=1, Down, // = 2 Left, // = 3 Right // = 4 }
Definieren eines Typs für eine Variable
String, Number, Boolean
let stageName: string = 'A Beatufil Vue'
let roomSize: number = 100
let isComplete: boolean = false
Convention: alle Typen werden bei der Zuweisung klein geschrieben
Array
let shoppingList :string[] = ['apple', 'bananas', 'cherries']
Functions
Hier müssen zwei Werte beachtet werden. Zum einen die eigentlichen Parameter und was die Funktion als Wert zurück gibt.
let generateFullName = (firstName: string, lastName: string): string => {
return firstname + ' ' + lastName;
}
Objects
Wie werden Typen auf Objekte angewendet?
// Ausgangsobjekt
let person = {
name: 'Peter Parker',
age: 20,
activeAvenger: true,
powers: ['wall-crawl', 'spider-sense']
}
// Objekt mit Typenzuweisung
let person: {
name: string;
age: number;
activeAvenger: boolean;
powers: string[];
} = {
name: 'Peter Parker',
age: 20,
activeAvenger: true,
powers: ['wall-crawl', 'spider-sense']
}
Custom Types
let buttonStyles: string = 'primary'
Typen
Typen / Types / Type ist ein Alias der beschreibt wie die Daten aussehen sollen bzw. wie ein Objekt strukturiert werden soll.
type buttonType = 'primary'
type buttonStyles: buttonType = 'primary' // WORKS
type buttonStyles: buttonType = 'secondary' // WONT WORK weil die Bescheibung
// an dieser Stelle nicht übereinstimmt.
// Definiert war, dass der Typ nur primary als Wert entgegenehmen darf.
// Hier wurde jedoch secondary übergeben.
Union Operator
Um mehrere Werte hinter einem Typ zu speichern, benötigt man den sog. Union Operator
. Dieser funktioniert intern, wie ein logisches Oder (||
). In unserem aktuellen Beispiel würde das in Code in etwa so aussehen.
if (buttonType === 'primary' || buttonType === 'secondary') {//execute}
Bei der Erstellung des Types wird der Union Operator |
wie folgt angewendet. Im kommenden Beispiel kann eine falsche Klassenzuweisung verhindert werden.
type buttonType = 'primary' | 'secondary' | 'success' | 'danger'
const errorBtnStlyes: buttonType = 'error' // WONT WORK!
const errorBtnStlyes: buttonType = 'danger' // WORKS!
Interface
Ist im Grunde genommen das Type
nur für Objekte. Der Bauplan für ein Objekt.
// Auszug aus vorherigem Kapitel
let person: {
name: string;
age: number;
activeAvenger: boolean;
powers: string[];
} = {
name: 'Peter Parker',
age: 20,
activeAvenger: true,
powers: ['wall-crawl', 'spider-sense']
}
// Use-case Interace - Generischer Umbau
interface Hero = {
name: string;
age: number;
activeAvenger: boolean;
powers: string[];
}
let person: Hero = {
name: 'Peter Parker',
age: 20,
activeAvenger: true,
powers: ['wall-crawl', 'spider-sense']
}
Können Typen mit Union Operatoren mit Interfaces kombiniert werden? – JA
type comicUniverse = 'Marvel' | 'DC'
interface Hero = {
name: string;
age: number;
activeAvenger: boolean;
powers: string[];
universe: comicUniverse;
}
let person: Hero = {
name: 'Peter Parker',
age: 20,
activeAvenger: true,
powers: ['wall-crawl', 'spider-sense'],
universe: 'Marvel'
}
Data with Custom Types
Extension VueDX: Plugin for advanced TypeScript/JavaScript support for Vue
// EventDetails.vue
<template>
<div v-if="event">
<h1>{{ event.title }}</h1>
<p>{{ event.time }} on {{ event.date }} @ {{ event.location }}</p>
<p>{{ event.description }}</p>
</div>
</template>
// EventDetails.vue
import { defineComponent } from 'vue'
export default defineComponent({
name: 'EventDetails',
data() {
return {
event: null
}
}
})
Problematik: Unser Editor/IDE ist nicht in der Lage festzustellen welche Attribute valide sind und welche nicht. Um dies uns sichtbar zu machen, können wir im Zusammenspiel mit [VueDX]() und einem Interface prüfen, ob die Daten & Attribute valide sind. Hierzu schauen wir uns das Objekt event
im obigen Beispiel genauer an.
Aktuell ist es ohne weiteres Möglich statt event.title
das Attribut event.heading
zu verwenden, obwohl dieses nicht existiert. Weder die IDE/ der Editor, noch uns selbst würde dieser Fehler auffallen. Um das zu verhindern können wir dem event ein Interace zuweisen um die Struktur vorzugeben.
Hierfür legen wir im src-Ordner eine neue Datei an. Diese nennen wir types.ts. In dieser Datei definieren wir ein Interface.
// types.ts
interface eventItem {
id: 123,
category: string,
title: string,
description: string
location: string,
date: string,
time: string,
organizer: string
}
und importieren diese im Anschluss in unsere Komponente
import { eventItem } from '../types'
Nun können wir mit der Zuweisung des Inferfaces zum Objekt beginnen.
Type Assertion
export default defineComponent({
name: 'EventDetails',
data() {
return {
event: null
}
},
})
Wenn wir das Interace nun zuweisen wollen, stellen wir fest das dieses Leer ist und nur über einen API-Call befüllt wird. Weisen wir nun das Interface zu, wird TypeScript sich daran stören, weil keine Daten in dem Objekt stehen, obwohl der "Bauplan" etwas anderes vorsieht.
An dieser Stelle müssen wir eingreifen und dem Compiler erklären, dass wir mehr über den Typ wissen als der Compiler selbst.
interface TodoItem {
label: string,
completed: boolean
}
const futureTodoItem = {}
futureTodoItem.label = 'Install VueDX extension'
futureTodoItem.completed = true
// TypeScript mag das nicht! Es geht davon aus das futureTodoItem
// ein leeres Objekt ist
Kurzes Beispiel zur Verdeutlichung!
Um an dieser Stelle den TypeScript und den Compiler zu überschreiben, machen wir uns Type Assertions
zu nutzen.
Mit as
können wir das Standardverhalten überschreiben und unseren Type dem Objekt zuweisen.
interface TodoItem {
label: string,
completed: boolean
}
const futureTodoItem = {} as TodoItem
futureTodoItem.label = 'Install VueDX extension'
futureTodoItem.completed = true
// TypeScript mag das nicht! Es geht davon aus das futureTodoItem
// ein leeres Objekt ist
In unserem Vue-Projekt sieht das dann wie folgt aus:
import { eventItem } from '../types'
export default defineComponent({
name: 'EventDetails',
data() {
return {
event: {} as eventItem
}
},
})
Props with Types
Zuweisung von Props soll man die Objektschreibweise verwenden
Beispiel:
// DONT
export default defineComponent({
props: ['id']
})
//DO INSTEAD
export default defineComponent({
props: {
id: {
type: Number,
required: true
}
},
})
What is generic?
Um Funktionen möglichst wiederverwendbar zu halten, kann man das Konzept von Generics
nutzen. Diese sind wie eine Art als Platzhalter für Typen. Hiermit können Funktionen möglichst "allgemein" gehalten werden.
Beispiel:
function createList(item: number): number[] {
const newList: number[] = []
newList.push(item)
return newList
}
const numberList = createList(123)
Diese Funktion ist sehr limitiert. Sie dient ausschließlich dem Zweck Nummern in ein neues Array zu packen und dieses dann zurückzugeben. Man könne die Funktion nun in addNumberToNumberList
umbenennen, aber das wäre nicht Zielführend und würde Redundanzen verursachen.
Mit Generics
würde der Code wie folgt aussehen
function createList<CustomType>(item: CustomType): CustomType[] {
const newList: CustomType[] = []
newList.push(item)
return newList
}
const numberList = createList<number>(123)
In der TypeScript-Community hat sich die Convention einzelne Buchstaben als Variable zu verwenden durchgesetzt.
function createList<T>(item: T): T[] {
const newList: T[] = []
newList.push(item)
return newList
}
const stringList = createList<number>(123)
In Vue
… nutzt man Generics wie folgt.
- Import der Methode
PropType
import { defineComponent, PropType } from 'vue'
import { EventItem } from '../types'
export default defineComponent({
props: {
event: {
type: Object as PropType<EventItem>,
required: true
}
},
})
Custom Types with Computed Properties
<script lang="ts">
import { defineComponent } from 'vue'
import { EventItem } from '../types'
export default defineComponent({
data() {
return {
events: [] as EventItem
}
},
methods: {
secondEvent(): EventItem {
return this.events[1]
}
}
})
</script>
For computed properties, focus on what type is returned
Custom Types with Methods
<script lang="ts">
import { defineComponent } from 'vue'
import { EventItem } from '../types'
export default defineComponent({
data() {
return {
events: [] as EventItem[]
}
},
methods: {
addEvent(newEvent) {
this.events.push(newEvent)
}
}
})
</script>
// Adding Custom Types to the Parameter of a Method
addEvent(newEvent: EventItem) {
this.events.push(newEvent)
}
// Adding Custom Types to the Return Value of a Method
secondEvent(): EventItem {
return this.events[1]
}
When it comes to adding custom types to methods, there are two key things to keep in mind:
- Do we need to add types to the parameters being passed into the method?
- Do we need to add types to whatever is being returned by the method?
For methods, add types for the parameters and return value if applicable
Weiter Quellen
https://v3.vuejs.org/guide/typescript-support.html#typescript-support