2023-06-06 20:13:11 +02:00
|
|
|
---
|
|
|
|
|
theme: gaia
|
|
|
|
|
_class: lead
|
|
|
|
|
paginate: true
|
2023-06-06 23:55:46 +02:00
|
|
|
backgroundColor: #00437a
|
|
|
|
|
color: #fff
|
2023-06-06 20:13:11 +02:00
|
|
|
---
|
|
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
# **Typescript**
|
2023-06-06 20:13:11 +02:00
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|

|
2023-06-06 20:13:11 +02:00
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
Endlich ruhig schlafen bei der Frontend-Entwicklung. 😴
|
2023-06-06 20:13:11 +02:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
# Was ist Typescript?
|
2023-06-06 20:13:11 +02:00
|
|
|
|
2023-07-03 15:16:41 +02:00
|
|
|
* Erweitert Javascript
|
|
|
|
|
* Fügt Typeninformationen in die Sprache ein
|
|
|
|
|
* Daten und Datenfluss wird besser beschrieben
|
2023-06-06 20:13:11 +02:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
# Fehlt es Javascript an Types?
|
2023-06-06 20:13:11 +02:00
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
```js
|
|
|
|
|
var data; // Was darf hier rein?
|
2023-06-06 23:55:46 +02:00
|
|
|
```
|
2023-06-30 18:54:33 +02:00
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Fehlt es Javascript an Types?
|
|
|
|
|
```js
|
|
|
|
|
> var data = "some string";
|
|
|
|
|
> console.log(data);
|
|
|
|
|
"some string"
|
|
|
|
|
> data = 15; // Why not both?
|
|
|
|
|
> console.log(data);
|
|
|
|
|
15
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Jetzt mit Types!
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Jetzt mit Types!
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
var data: string = "some string";
|
|
|
|
|
console.log(data);
|
|
|
|
|
data = 15; // ❌ Error
|
|
|
|
|
console.log(data);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Transparenz von Schnittstellen
|
|
|
|
|
|
|
|
|
|
Was tut diese Library?
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[... Implementierungsdetails]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Transparenz von Schnittstellen
|
|
|
|
|
|
|
|
|
|
Was tut diese Library?
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[... Implementierungsdetails]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Besser: Wie benutze ich diese Library?
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Transparenz von Schnittstellen
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[... Implementierungsdetails]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// your-module.js
|
|
|
|
|
drawGraph([{
|
|
|
|
|
x: 2,
|
|
|
|
|
y: 5,
|
|
|
|
|
}, {
|
|
|
|
|
x: 3,
|
|
|
|
|
y: 7,
|
|
|
|
|
}]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Transparenz von Schnittstellen
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[... Implementierungsdetails]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// your-module.js
|
|
|
|
|
drawGraph([
|
|
|
|
|
5,
|
|
|
|
|
7
|
|
|
|
|
]);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Transparenz von Schnittstellen
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[... Implementierungsdetails]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// your-module.js
|
|
|
|
|
drawGraph([
|
|
|
|
|
5,
|
|
|
|
|
7
|
|
|
|
|
], {
|
|
|
|
|
start: 0,
|
|
|
|
|
stepSize: 1,
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Dafür hat man doch JSDoc!
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// @iserv/graph.js
|
|
|
|
|
/**
|
|
|
|
|
* @typedef {Object} GraphOptions
|
|
|
|
|
* @property {HTMLCanvasElement} mount
|
|
|
|
|
* @property {number} start
|
|
|
|
|
* @property {number} stepSize
|
2023-07-02 19:41:56 +02:00
|
|
|
*
|
2023-06-30 18:54:33 +02:00
|
|
|
* @param {GraphOptions} options
|
|
|
|
|
*/
|
|
|
|
|
export function drawGraph(data, options = {}) {
|
|
|
|
|
[...Implementierungsdetail]
|
|
|
|
|
}
|
|
|
|
|
// your-module.js
|
|
|
|
|
drawGraph(
|
2023-07-02 19:41:56 +02:00
|
|
|
data,
|
2023-06-30 18:54:33 +02:00
|
|
|
{
|
|
|
|
|
mount: document.getElementById('my-canvas'),
|
|
|
|
|
start: 2,
|
|
|
|
|
stepSize: 1,
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Warum nicht JSDoc?
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
* JSDoc kann lügen
|
|
|
|
|
* Wird nicht automatisch überprüft
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typescript aufsetzen
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
* In neuem Projekt
|
|
|
|
|
* Integration mit Webpack
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typescript in einem neuen Projekt
|
|
|
|
|
|
|
|
|
|
Installieren
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
npm install -g typescript # global
|
|
|
|
|
npm install --save-dev typescript # lokal
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typescript in einem neuen Projekt
|
|
|
|
|
|
|
|
|
|
Neues Projekt
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
tsc --init
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* (Lokal ist der Befehl dann `npx tsc [...]`)
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Konfiguration
|
|
|
|
|
|
|
|
|
|
tsconfig.json
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"compilerOptions": {
|
|
|
|
|
"target": "es2016",
|
|
|
|
|
"module": "commonjs",
|
|
|
|
|
"esModuleInterop": true,
|
|
|
|
|
"forceConsistentCasingInFileNames": true,
|
|
|
|
|
"strict": true,
|
|
|
|
|
"skipLibCheck": true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Kompilieren
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
tsc [<file>]
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Kompilieren
|
|
|
|
|
```ts
|
|
|
|
|
// index.ts
|
|
|
|
|
let variable: number = 5;
|
|
|
|
|
|
|
|
|
|
variable = 10;
|
|
|
|
|
```
|
|
|
|
|
<one `tsc` later>
|
|
|
|
|
```js
|
|
|
|
|
// index.js
|
|
|
|
|
"use strict";
|
|
|
|
|
let variable = 5;
|
|
|
|
|
variable = 10;
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
## Integration in Webpack-Projekten
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
## Integration in Webpack-Projekten
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Integration in Webpack-Projekten
|
|
|
|
|
```
|
|
|
|
|
npm install --save-dev typescript ts-loader
|
|
|
|
|
^^^^^^^^^
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
## Integration in Webpack-Projekten
|
|
|
|
|
```js
|
|
|
|
|
// webpack.config.js
|
|
|
|
|
module.exports = {
|
|
|
|
|
module: {
|
|
|
|
|
rules: [
|
|
|
|
|
{
|
|
|
|
|
test: /\.tsx?$/,
|
|
|
|
|
use: 'ts-loader',
|
|
|
|
|
exclude: /node_modules/,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
resolve: {
|
|
|
|
|
extensions: ['.tsx', '.ts', '.js'],
|
|
|
|
|
},
|
|
|
|
|
// [...]
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Integration in bestehende Projekte
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2023-07-03 14:04:17 +02:00
|
|
|
## Integration in bestehende Projekte
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2023-06-30 18:54:33 +02:00
|
|
|
## Typescript in Javascript?
|
|
|
|
|
|
|
|
|
|
* TS in JS integrieren geht ohne Probleme
|
|
|
|
|
* ```ts
|
|
|
|
|
// extension.ts
|
|
|
|
|
export function getFileExtension(path: string): string {
|
|
|
|
|
return path.split(".").slice(-1);
|
|
|
|
|
}
|
|
|
|
|
// path.js
|
|
|
|
|
import {getFileExtension} from './extension.ts';
|
|
|
|
|
console.log(getFileExtension("some/image.png"));
|
|
|
|
|
// > "png"
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Javascript in Typescript?
|
|
|
|
|
* JS in TS integrieren ist schwierig
|
|
|
|
|
* ```ts
|
|
|
|
|
// extension.js
|
|
|
|
|
export function getFileExtension(path) {
|
|
|
|
|
return path.split(".").slice(-1);
|
|
|
|
|
}
|
|
|
|
|
// path.ts
|
|
|
|
|
import {getFileExtension} from './extension.ts';
|
|
|
|
|
console.log(getFileExtension("some/image.png"));
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
## Javascript in Typescript
|
|
|
|
|
```
|
|
|
|
|
path.ts:1:32 - error TS7016: Could not find a declaration file
|
|
|
|
|
for module './extension.js'.
|
|
|
|
|
'./extension.js' implicitly has an 'any' type.
|
|
|
|
|
|
|
|
|
|
1 import {getFileExtension} from './extension.js';
|
|
|
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
|
Found 1 error in path.ts:1
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Javascript in Typescript
|
|
|
|
|
|
|
|
|
|
Wie damit umgehen?
|
|
|
|
|
* Typescript auf weniger strikt stellen
|
2023-07-02 19:41:56 +02:00
|
|
|
* => Nutzung von Typescript verliert an Wert
|
2023-06-30 18:54:33 +02:00
|
|
|
* Alle Abhängigkeiten direkt nach Typescript konvertieren
|
2023-07-02 19:41:56 +02:00
|
|
|
* Nicht immer machbar
|
2023-06-30 18:54:33 +02:00
|
|
|
* Schnittstellen mit Typen versehen
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Schnittstellen typisieren
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// extension.d.ts
|
|
|
|
|
export function getFileExtension(path: string): string;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* Keine Implementierung
|
|
|
|
|
* Kein Type-Checking der Implementierung
|
|
|
|
|
* Schnittstellen werden wenigstens typisiert `¯\_(ツ)_/¯`
|
|
|
|
|
* Das geht auch mit JS-Libraries
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
2023-07-02 09:41:35 +02:00
|
|
|
## Wie beschreibe ich nun meine Daten?
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const workerBee = {
|
|
|
|
|
role: 'worker',
|
|
|
|
|
name: 'Larry',
|
|
|
|
|
age: 15,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Objekte
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const workerBee: Bee = {
|
|
|
|
|
role: 'worker',
|
|
|
|
|
name: 'Larry',
|
|
|
|
|
age: 15,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Bee = {
|
|
|
|
|
role: string,
|
|
|
|
|
name: string,
|
|
|
|
|
age: number,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Semantische Typen
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const workerBee: Bee = {
|
|
|
|
|
role: 'worker',
|
|
|
|
|
name: 'Larry',
|
|
|
|
|
age: 15,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Bee = {
|
|
|
|
|
role: BeeRole,
|
|
|
|
|
name: string,
|
|
|
|
|
age: Months,
|
|
|
|
|
};
|
|
|
|
|
type BeeRole = 'worker'|'queen'|'guard';
|
|
|
|
|
type Months = number;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Dictionaries
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const userNames = {
|
|
|
|
|
'652a4707-3043-4ca9-b417-24feac0f5953': 'Bernhard',
|
|
|
|
|
'017daef7-e1f1-4160-81ff-e5b17e86fc6b': 'Frieda',
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Dictionaries
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const userNames: UserNameDictionary = {
|
|
|
|
|
'652a4707-3043-4ca9-b417-24feac0f5953': 'Bernhard',
|
|
|
|
|
'017daef7-e1f1-4160-81ff-e5b17e86fc6b': 'Frieda',
|
|
|
|
|
};
|
|
|
|
|
type UserId = string;
|
|
|
|
|
type UserNameDictionary = Record<UserId, string>;
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Dictionaries (alternative)
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const userNames: UserNameDictionary = {
|
|
|
|
|
'652a4707-3043-4ca9-b417-24feac0f5953': 'Bernhard',
|
|
|
|
|
'017daef7-e1f1-4160-81ff-e5b17e86fc6b': 'Frieda',
|
|
|
|
|
};
|
|
|
|
|
type UserId = string;
|
|
|
|
|
type UserNameDictionary = {
|
|
|
|
|
[UserId]: string;
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Optionale Properties
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const lkw = {
|
|
|
|
|
anzahlRaeder: 18,
|
|
|
|
|
hubraum: 25,
|
|
|
|
|
};
|
|
|
|
|
const bobbycar = {
|
|
|
|
|
anzahlRaeder: 4,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Optionale Properties
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const lkw: Car = {
|
|
|
|
|
anzahlRaeder: 18,
|
|
|
|
|
hubraum: 25,
|
|
|
|
|
};
|
|
|
|
|
const bobbycar: Car = {
|
|
|
|
|
anzahlRaeder: 4,
|
|
|
|
|
};
|
|
|
|
|
type Kubikmeter = number;
|
|
|
|
|
type Car = {
|
2023-07-02 19:41:56 +02:00
|
|
|
anzahlRaeder: number,
|
|
|
|
|
hubraum?: Kubikmeter,
|
2023-07-02 09:41:35 +02:00
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Nur optionale Properties
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Vehicle = Partial<{
|
2023-07-02 19:41:56 +02:00
|
|
|
anzahlRaeder: number,
|
|
|
|
|
hubraum: Kubikmeter,
|
|
|
|
|
anzahlKufen: number,
|
2023-07-02 09:41:35 +02:00
|
|
|
}>;
|
|
|
|
|
```
|
|
|
|
|

|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Nur optionale Properties
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Vehicle = Partial<{
|
2023-07-02 19:41:56 +02:00
|
|
|
anzahlRaeder: number,
|
|
|
|
|
hubraum: Kubikmeter,
|
|
|
|
|
anzahlKufen: number,
|
2023-07-02 09:41:35 +02:00
|
|
|
}>;
|
|
|
|
|
const helikopter: Vehicle = {
|
|
|
|
|
anzahlKufen: 2,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Nur optionale Properties
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Vehicle = Partial<{
|
2023-07-02 19:41:56 +02:00
|
|
|
anzahlRaeder: number,
|
|
|
|
|
hubraum: Kubikmeter,
|
|
|
|
|
anzahlKufen: number,
|
2023-07-02 09:41:35 +02:00
|
|
|
}>;
|
|
|
|
|
const helikopter: Vehicle = {
|
|
|
|
|
anzahlKufen: 2,
|
|
|
|
|
};
|
|
|
|
|
const bobbyCar: Vehicle = {
|
|
|
|
|
anzahlRaeder: 4,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Nur optionale Properties
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Vehicle = Partial<{
|
2023-07-02 19:41:56 +02:00
|
|
|
anzahlRaeder: number,
|
|
|
|
|
hubraum: Kubikmeter,
|
|
|
|
|
anzahlKufen: number,
|
2023-07-02 09:41:35 +02:00
|
|
|
}>;
|
|
|
|
|
const helikopter: Vehicle = {
|
|
|
|
|
anzahlKufen: 2,
|
|
|
|
|
};
|
|
|
|
|
const bobbyCar: Vehicle = {
|
|
|
|
|
anzahlRaeder: 4,
|
|
|
|
|
};
|
|
|
|
|
const luftKissenFahrzeug: Vehicle = {
|
|
|
|
|
hubraum: 10,
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Readonly
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// @iserv/colors.ts
|
|
|
|
|
export type Color = string;
|
|
|
|
|
export type Colors = Record<'RED'|'GREEN'|'BLUE', Color>
|
|
|
|
|
export const colors: Colors = {
|
|
|
|
|
RED: '#ff0000',
|
|
|
|
|
GREEN: '#00ff00',
|
|
|
|
|
BLUE: '#0000ff',
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Readonly
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// @iserv/colors.ts
|
|
|
|
|
export type Color = string;
|
|
|
|
|
export type Colors = Record<'RED'|'GREEN'|'BLUE', Color>
|
|
|
|
|
export const colors: Colors = {
|
|
|
|
|
RED: '#ff0000',
|
|
|
|
|
GREEN: '#00ff00',
|
|
|
|
|
BLUE: '#0000ff',
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
```ts
|
|
|
|
|
// consumer.ts
|
|
|
|
|
import {colors} from '@iserv/colors.ts';
|
|
|
|
|
colors = {}; // verboten wegen const
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Readonly
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// @iserv/colors.ts
|
|
|
|
|
export type Color = string;
|
|
|
|
|
export type Colors = Record<'RED'|'GREEN'|'BLUE', Color>
|
|
|
|
|
export const colors: Colors = {
|
|
|
|
|
RED: '#ff0000',
|
|
|
|
|
GREEN: '#00ff00',
|
|
|
|
|
BLUE: '#0000ff',
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
```ts
|
|
|
|
|
// consumer.ts
|
|
|
|
|
import {colors} from '@iserv/colors.ts';
|
|
|
|
|
colors = {}; // verboten wegen const
|
|
|
|
|
colors.BLUE = colors.RED; // erlaubt 😢
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Readonly
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
// @iserv/colors.ts
|
|
|
|
|
export type Color = string;
|
|
|
|
|
export type Colors = Record<'RED'|'GREEN'|'BLUE', Color>
|
|
|
|
|
export const colors: Readonly<Colors> = {
|
|
|
|
|
RED: '#ff0000', ^^^^^^^^
|
|
|
|
|
GREEN: '#00ff00',
|
|
|
|
|
BLUE: '#0000ff',
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
```ts
|
|
|
|
|
// consumer.ts
|
|
|
|
|
import {colors} from '@iserv/colors.ts';
|
|
|
|
|
colors = {}; // verboten wegen const
|
|
|
|
|
colors.BLUE = colors.RED; // verbiet 😎
|
|
|
|
|
```
|
|
|
|
|
|
2023-07-02 19:41:56 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Renderable = {
|
|
|
|
|
render: () => void,
|
|
|
|
|
}
|
|
|
|
|
type Updateable = {
|
|
|
|
|
update: () => void,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const gameObject = {
|
|
|
|
|
render: () => {},
|
|
|
|
|
update: () => {},
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren - Union
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type Renderable = {
|
|
|
|
|
render: () => void,
|
|
|
|
|
}
|
|
|
|
|
type Updateable = {
|
|
|
|
|
update: () => void,
|
|
|
|
|
}
|
|
|
|
|
type GameObject = Renderable & Updateable;
|
|
|
|
|
const gameObject: GameObject = {
|
|
|
|
|
render: () => {},
|
|
|
|
|
update: () => {},
|
|
|
|
|
};
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type NetworkPacket = {
|
|
|
|
|
data: Record<string, string>,
|
|
|
|
|
msg: string,
|
|
|
|
|
};
|
|
|
|
|
type Exception = {
|
|
|
|
|
trace: string[],
|
|
|
|
|
msg: string,
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren - Intersection
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type NetworkPacket = {
|
|
|
|
|
data: Record<string, string>,
|
|
|
|
|
msg: string,
|
|
|
|
|
};
|
|
|
|
|
type Exception = {
|
|
|
|
|
trace: string[],
|
|
|
|
|
msg: string,
|
|
|
|
|
}
|
|
|
|
|
type FetchResult = NetworkPacket | Exception;
|
|
|
|
|
let fetch: () => FetchResult;
|
|
|
|
|
const fetchResult: FetchResult = fetch();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren - Intersection
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type NetworkPacket = {
|
|
|
|
|
data: Record<string, string>,
|
|
|
|
|
msg: string,
|
|
|
|
|
};
|
|
|
|
|
type Exception = {
|
|
|
|
|
trace: string[],
|
|
|
|
|
msg: string,
|
|
|
|
|
}
|
|
|
|
|
type FetchResult = NetworkPacket | Exception;
|
|
|
|
|
let fetch: () => FetchResult;
|
|
|
|
|
const fetchResult: FetchResult = fetch();
|
|
|
|
|
console.log(fetchResult.msg);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren - Intersection
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type NetworkPacket = {
|
|
|
|
|
data: Record<string, string>,
|
|
|
|
|
msg: string,
|
|
|
|
|
};
|
|
|
|
|
type Exception = {
|
|
|
|
|
trace: string[],
|
|
|
|
|
msg: string,
|
|
|
|
|
}
|
|
|
|
|
type FetchResult = NetworkPacket | Exception;
|
|
|
|
|
let fetch: () => FetchResult;
|
|
|
|
|
const fetchResult: FetchResult = fetch();
|
|
|
|
|
if ('trace' in fetchResult) {
|
|
|
|
|
// fetchResult is Exception
|
|
|
|
|
console.log(fetchResult.trace);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Typen kombinieren - Intersection
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
type NetworkPacket = {
|
|
|
|
|
data: Record<string, string>,
|
|
|
|
|
msg: string,
|
|
|
|
|
};
|
|
|
|
|
type Exception = {
|
|
|
|
|
trace: string[],
|
|
|
|
|
msg: string,
|
|
|
|
|
}
|
|
|
|
|
type FetchResult = NetworkPacket | Exception;
|
|
|
|
|
let fetch: () => FetchResult;
|
|
|
|
|
const fetchResult: FetchResult = fetch();
|
|
|
|
|
if ('data' in fetchResult) {
|
|
|
|
|
// fetchResult is NetworkPacket
|
|
|
|
|
console.log(fetchResult.data);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Duck Typing
|
|
|
|
|
|
2023-07-03 14:04:17 +02:00
|
|
|

|
|
|
|
|
|
|
|
|
|
* > *If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.*
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Type narrowing
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const ducks: unknown = await fetch('https://api.ducks.com/sorts.json');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Type narrowing
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const ducks: unknown = await fetch('https://api.ducks.com/sorts.json');
|
|
|
|
|
type DuckSort = {
|
|
|
|
|
name: string,
|
|
|
|
|
id: number,
|
|
|
|
|
species: string,
|
|
|
|
|
habitat: string[],
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Type narrowing via Type-Guards
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const ducks: unknown = await fetch('https://api.ducks.com/sorts.json');
|
|
|
|
|
type DuckSort = {
|
|
|
|
|
name: string,
|
|
|
|
|
id: number,
|
|
|
|
|
species: string,
|
|
|
|
|
habitat: string[],
|
|
|
|
|
}
|
|
|
|
|
function isDuckSort(duckSort: unknown): duckSort is DuckSort {
|
|
|
|
|
if (!('name' in duckSort) || typeof duckSort['name'] !== 'string') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// [...]
|
|
|
|
|
}
|
|
|
|
|
if (isDuckSort(ducks)) {
|
|
|
|
|
// 'ducks' can be used now
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Automatische Type-Guard Generierung
|
|
|
|
|
|
|
|
|
|
* `github.com/rhys-vdw/ts-auto-guard`
|
|
|
|
|
* Generiert Type-Guards automatisch
|
|
|
|
|
* ```ts
|
|
|
|
|
/** @see {isDuckSort} ts-auto-guard:type-guard */
|
|
|
|
|
export type DuckSort = {/** ... */};
|
|
|
|
|
```
|
|
|
|
|
* `npx ts-auto-guard`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ts-auto-guard Beispiel
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
export function isDuckSort(obj: unknown): obj is DuckSort {
|
|
|
|
|
const typedObj = obj as DuckSort
|
|
|
|
|
return (
|
|
|
|
|
(typedObj !== null &&
|
|
|
|
|
typeof typedObj === "object" ||
|
|
|
|
|
typeof typedObj === "function") &&
|
|
|
|
|
typeof typedObj["name"] === "string" &&
|
|
|
|
|
typeof typedObj["id"] === "number" &&
|
|
|
|
|
typeof typedObj["species"] === "string" &&
|
|
|
|
|
Array.isArray(typedObj["habitat"]) &&
|
|
|
|
|
typedObj["habitat"].every((e: any) =>
|
|
|
|
|
typeof e === "string"
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Bad Practices
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const config = readFromFile('config.yml') as Config;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* `as`-Operator ist der Holzhammer
|
|
|
|
|
* Typescript glaubt dir alles
|
|
|
|
|
* Führt potenzielle Bugs ein
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## `as`-Operator
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const number = {name: 'blarg'} as number;
|
|
|
|
|
```
|
|
|
|
|
* 
|
|
|
|
|
* Fauler Ersatz für Type-Guarding
|
|
|
|
|
* Panzerband für Typescripts Deduktions-Schwächen
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Deduktionsschwächen
|
|
|
|
|
|
2023-07-03 15:16:41 +02:00
|
|
|
```ts
|
|
|
|
|
function inferBroken(str: string) {
|
|
|
|
|
if (['Bernd', 'Margeret'].includes(str)) {
|
|
|
|
|
const narrowed: 'Bernd'|'Margeret' = str;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Deduktionsschwächen
|
|
|
|
|
|
2023-07-03 14:04:17 +02:00
|
|
|
```ts
|
|
|
|
|
function inferBroken(str: string) {
|
|
|
|
|
if (['Bernd', 'Margeret'].includes(str)) {
|
|
|
|
|
const narrowed: 'Bernd'|'Margeret' = str;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
```
|
|
|
|
|
error TS2322: Type 'string' is not assignable to type '"Bernd" | "Margeret"'.
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Deduktionsschwächen - Do NOT
|
|
|
|
|
```ts
|
|
|
|
|
function inferBroken(str: string) {
|
|
|
|
|
if (['Bernd', 'Margeret'].includes(str)) {
|
|
|
|
|
const narrowed = str as 'Bernd'|'Margeret';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Deduktionsschwächen - Do NOT
|
|
|
|
|
```ts
|
|
|
|
|
function inferBroken(str: string) {
|
|
|
|
|
if (['Jürgen', 'Marilla'].includes(str)) {
|
|
|
|
|
const narrowed = str as 'Bernd'|'Margeret'; // Wirft keine Fehler 😢
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
* Mit guard-clauses wäre das nicht passiert
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Reicht jetzt auch
|
|
|
|
|
|
|
|
|
|

|