React mit TypeScript nutzen
react | Jack Franklin • | 17 Minuten
Ich bin neulich auf den Geschmack von TypeScript gekommen und habe darüber in vielen Blogs von Tom Dale und anderen gelesen. Heute zeige ich dir wie ich ein TypeScript Projekt von Grund auf mit React und Webpack, um den Build-Process zu verwalten, neu eingerichtet habe. Ich werde dir auch meine ersten Eindrücke von TypeScript geben und insbesondere wie du React mit TypeScript nutzen kannst.
Ich werde nicht zu sehr ins Detail der Besonderheiten der TypeScript Syntax eingehen, aber du kannst entweder das TypeScript Handbook oder das kostenlose Buch TypeScript Deep Dive, welches dir auch eine großartige Einführung in die Sprache geben wird, lesen.
TypeScript installieren und konfigurieren
Das erste was zu tun war, ist TypeScript lokal in mein node_modules
zu installieren, was ich mit yarn
gemacht habe. Zuerst habe ich also mit yarn init
ein neues Projekt erstellt:
yarn init
yarn add typescript
Wenn du TypeScript installierst, bekommst du das tsc
command line tool, welches TypeScript kompilieren kann und auch einen start tsconfig.json
, den du bearbeiten kannst. Das kannst du, indem du tsc --init
ausführst– wenn du TypeScript lokal installiert hast, dann musst du ./node_modules/.bin/tsc --init
ausführen.
Anmerkung: Ich habe das Verzeichnis ./node_modules/.bin
auf meinem $PATH
, welches du in meinen dotfiles finden kannst. Das ist etwas riskant, da ich versehentlich irgendeine ausführbare Datei aus diesem Verzeichnis ausführen könnte, aber ich gehe das Risiko ein, weil ich weiß was ich lokal installiert habe und das spart mir sehr viel getippe!
tsc --init
generiert ein tsconfig.json
, dort sind alle configs der Kompilierer von TypeScript. Es gibt ein paar Änderungen, die ich an der default config vorgenommen habe und diejenige die ich verwende, siehst du unten:
{
"compilerOptions": {
"module": "es6", // use ES2015 modules
"target": "es6", // compile to ES2015 (Babel will do the rest)
"allowSyntheticDefaultImports": true, // see below
"baseUrl": "src", // enables you to import relative to this folder
"sourceMap": true, // make TypeScript generate sourcemaps
"outDir": "ts-build", // output directory to build to (irrelevant because we use Webpack most of the time)
"jsx": "preserve", // enable JSX mode, but "preserve" tells TypeScript to not transform it (we'll use Babel)
"strict": true,
},
"exclude": [
"node_modules" // don't run on any code in the node_modules directory
]
}
allowSyntheticDefaultImports
Mit dieser Regel kannst du ES2015 style default imports
benutzen, sogar dann, wenn der Code, den du importierst, keinen ES2015 default export hat.
Das geschieht beim importieren, zum Beispiel wenn du React importierst, dessen Code nicht in ES2015 geschrieben ist (Der source code ist es, aber React liefert eine eingebaute Version). Das bedeutet, technisch gesehen hat er keinen ES2015 default export
, also wird TypeScript dir sagen, wenn du ihn importierst. Allerdings sind Build-Tools wie Webpack in oder Lage, das richtige zu importieren, also schalte ich die Option an, weil ich import React from 'react'
gegenüber import * as React from'react'
bevorzuge.
strict: true
TypeScript version 2.3 hat eine neue config Option eingeführt, strict
. Wenn eingeschaltet, dann konfiguriert der TypeScript-Compiler so streng wie möglich – das ist vielleicht nicht was du willst, wenn du einen JS an TS portierst, aber für neue Projekte ist es sinnvoll so „streng“ wie möglich zu sein. Dies schaltet ein paar verschiedene Einstellungen ein, die erwähnenswertesten sind noImplicitAny
und strictNullChecks
:
noImplicitAny
Oftmals, wenn du TypeScript zu einem vorhandenen Projekt hinzufügen möchtest, macht es dir TypeScript einfach, indem es keine Fehlermeldung auswirft, wenn du die Variablentypen nicht deklarierst. Allerdings möchte ich, dass der Compiler so streng wie möglich ist, wenn ich ein neues TypeScript Projekt aufbaue.
Eines der Dinge, die TypeScript standartmäßig ausführt ist den any
Typ zu Variablen anfügt. any
ist eine effektive Methode in TypeScript die sagt „type-checke das nicht, es kann jeder Wert sein“. Das ist nützlich, wenn du JavaScript portierst, aber es ist besser strikt zu sein, wenn du kannst. Mit dieser Einstellung die auf true
gesetzt ist, kannst du keine Deklarationen verpassen. Zum Beispiel wird dir dieser Code einen Error geben, wenn noImplicitAny
auf true
gesetzt ist:
function log(thing) {
console.log('thing', thing)
}
Du kannst darüber mehr in TypeScript Deep Dive lesen.
strictNullChecks
Das ist eine weitere Option, um den TypeScript-Compiler strikter zu gestalten. Das TypeScript Deep Dive Buch beinhaltet eine große Sektion über diese Option.
Mit dieser Option wird TypeScript mehr Fälle erkennen, bei denen du auf einen Wert verweist, der undefiniert sein könnte. Es wird dir, zum Beispiel, ein Fehler zurückgeben:
person.age.increment()
Mit strictNullChecks
aktiv, wirft Typescript eine Fehlermeldung aus und stellt sicher, dass du dich darum kümmerst, wenn TypeScript denkt, dass person
oder person.age
undefiniert
ist. Das verhindert runtime Fehler, deshalb scheint es eine ziemlich gute Option zu sein.
Webpack, Babel und TypeScript einstellen
Ich bin ein großer Fan von Webpack; ich genieße das Ökosystem der verfügbaren Plugins, ich mag den Workflow der Developer und es ist gut bei der Verwaltung komplexer Anwendungen. Deshalb würde ich Webpack hinzufügen, auch wenn wir einfach den TypeScript-Compiler verwenden könnten. Wir brauchen auch Babel, weil der TypeScript Compiler ES2015 + React für uns ausgibt, also lassen wir Babel die Arbeit für uns machen. Lass uns Webpack, Babel und die nötigen Presets installieren, zusammen mit dem ts-loader und dem Webpack Plugin für TypeScript.
Es gibt auch einen awesome-typeScript-loader, aber ich habe ts-loader
zuerst gefunden und bisher funktioniert der super. Ich würde gerne von irgendjemandem der den awesome-typescript-loader
benutzt hören, wie die Vergleiche sind.
yarn add webpack babel-core babel-loader babel-preset-es2015 babel-preset-react ts-loader webpack-dev-server
An dieser Stelle möchte ich Tom Duncalf danken, Blog-Post zu TypeScript 1.9 + React war ein genialer Startpunkt für mich, den ich wärmstens empfehlen kann.
Es gibt nichts Überraschendes in der Webpack-config, ich habe dem Code einige Kommentare beigefügt:
const webpack = require('webpack')
const path = require('path')
module.exports = {
// put sourcemaps inline
devtool: 'eval',
// entry point of our application, within the `src` directory (which we add to resolve.modules below):
entry: [
'index.tsx'
],
// configure the output directory and publicPath for the devServer
output: {
filename: 'app.js',
publicPath: 'dist',
path: path.resolve('dist')
},
// configure the dev server to run
devServer: {
port: 3000,
historyApiFallback: true,
inline: true,
},
// tell Webpack to load TypeScript files
resolve: {
// Look for modules in .ts(x) files first, then .js
extensions: ['.ts', '.tsx', '.js'],
// add 'src' to the modules, so that when you import files you can do so with 'src' as the relative route
modules: ['src', 'node_modules'],
},
module: {
loaders: [
// .ts(x) files should first pass through the Typescript loader, and then through babel
{ test: /\.tsx?$/, loaders: ['babel-loader', 'ts-loader'], include: path.resolve('src') }
]
},
}
Wir konfigurieren die Loader so dass jede .ts(x)
Datei zuerst durch den ts-loader
geht - Dieser kompiliert es mit TypeScript, indem es die Einstellungen in unserer tsconfig.json
benutzt – und ES2015
zu emmitieren. Dann benutzen wir Babel um es runter zu ES5 zu konvertieren. Um das zu schaffen, erstelle ich eine .babelrc
die die Voreinstellungen hat die wir benötigen:
{
"presets": ["es2015", "react"]
}
Und damit sind wir jetzt in der Lage unsere TypeScript Anwendung zu schreiben.
Eine TypeScript React Komponente schreiben
Jetzt sind wir bereit um src/index.tsx
zu erstellen, das wird der Einstiegspunkt unserer Anwendung.
Jetzt können wir zunächst eine Dummy-Komponente erstellen und rendern, um zu checken, ob alles funktioniert.
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => {
return (
<div>
<p>Hello world!</p>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
Wenn du jetzt Webpack laufen lässt, siehst du einige Fehler:
ERROR in ./src/index.tsx
(1,19): error TS2307: Cannot find module 'react'.
ERROR in ./src/index.tsx
(2,22): error TS2307: Cannot find module 'react-dom'.
Das passiert, weil TypeScript versucht den Typ von React herauszufinden. Das gleiche versucht er also für React DOM zu machen. React ist nicht in TypeScript verfasst, deshalb enthält es diese Information nicht. Aber zum Glück hat die Community für diesen Fall DefinitelyTyped erstellt, ein großes Repertoir an Typen und Modulen.
Der Installations-Mechanismus hat sich kürzlich geändert; alle Typen werden unter dem Bereich @types
gespeichert. Um die Typen für React und ReactDOM zu bekommen, führen wir folgendes aus:
yarn add @types/react
yarn add @types/react-dom
Und damit verschwinden die Fehler. Immer wenn du eine Abhängigkeit installierst, kannst du immer versuchen das @types
Paket auch zu installieren, oder wenn du sehen willst ob die Typen verfügbar sind, kannst du die TypeSearch Webseite benutzen.
Die Anwendung lokal ausführen
Um die Anwendung lokal auszurühren müssen wir einfach nur den webpack-dev-server
Befehl ausführen.
Ich habe dafür ein Shortcut start
erstellt:
"scripts": {
"start": "webpack-dev-server"
}
Der Dev-Server wird die webpack.config.json
Datei finden und sie benutzen, um unsere Anwendung zu bauen.
Wenn du yarn start
ausführst wirst du den Output vom Server sehen, eingeschlossen den Output vom ts-loader
, der bestätigt, dass alles funktioniert.
$ webpack-dev-server
Project is running at http://localhost:3000/
webpack output is served from /dist
404s will fallback to /index.html
ts-loader: Using typescript@2.3.0 and /Users/jackfranklin/git/interactive-react-introduction/tsconfig.json
Version: webpack 2.4.1
Time: 6077ms
Asset Size Chunks Chunk Names
app.js 1.14 MB 0 [emitted] [big] main
webpack: Compiled successfully.
Um es lokal zu sehen, kann ich eine index.html
Datei erstellen, die unseren kompilierten Code lädt:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Typescript App</title>
</head>
<body>
<div id="app"></div>
<script src="dist/app.js"></script>
</body>
</html>
Du solltest Hello world!
auf Port 3000 sehen, dann läuft TypeScript!
Ein Modul eingeben
Für ein Projekt an dem ich gearbeitet habe, wollte ich das React Ace Modul verwenden, um einen Code Editor in mein Projekt einzufügen. Allerdings hat das Modul keine Typen dafür zur Verfügung gestellt und es gibt auch kein @types/react-ace
. In diesem Fall müssen wir Typen zu unserer Anwendung hinzufügen, damit TypeScript weiß, wie man es eingibt. Das mag vielleicht ärgerlich klingen, aber die Vorteile, dass TypeScript zumindest ein bisschen über alle Drittanbieter-Abhängigkeiten weiß, wird dir viel Debugging-Zeit ersparen.
Um eine Datei zu definieren, die gerade Typen hat, hängst du ihr .d.ts
(das „d“ steht für „declaration“) an. Du kannst darüber mehr auf den TypeScript docs lesen. TypeScript wird diese Dateien in deinem Projekt automatisch finden. Du musst sie nicht explizit importieren.
Ich habe die Datei react-ace.d.ts
erstellt, der das Module erstellt und ihren default expor
als eine React Komponente definiert:
declare module 'react-ace' {
interface ReactAceProps {
mode: string
theme: string
name: string
editorProps?: {}
showPrintMargin?: boolean
minLines?: number
maxLines?: number
wrapEnabled?: boolean
value: string
highlightActiveLine?: boolean
width?: string
fontSize?: number
}
const ReactAce: React.ComponentClass<ReactAceProps>
export = ReactAce
}
Zuerst erstelle ich ein TypeScript Interface für die Eigenschaften, welche die Komponente benötigt, dann musst ich nur noch export = ReactAce
deklarieren. Durch die Eingabe der Eigenschaften wird TypeScript mir sagen, wenn ich eine Eigenschaft falsch geschrieben habe oder vergessen habe, die wichtig ist.
Testen
Zum Schluss wollte ich auch ein gutes Testing Setup mit TypeScript haben. Ich bin ein großer Fan von Facebooks Jest und habe ein bisschen gegooglet, um herauszufinden, ob ich es mit TypeScript starten kann. Es hat sich herausgestellt, dass es sehr gut möglich ist, und da gibt es sogar das ts-jest Paket, dass all die schweren Arbeiten erledigt. Darüber hinaus gibt es da noch das @types/jest
Paket, damit kannst du auch all deine Tests type-checken lassen kannst.
Großes Dankeschön an RJ Zaworski, dessen Post über TypeScript und Jest der mich in dieses Thema eingeführt hat. Sobald du ts-jest
installiert hast, musst du Jest nur noch konfigurieren, das machst du in package.json
mit diesen Einstellungen:
"jest": {
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "/*.spec.(ts|tsx|js)$"
},
Die erste Einstellung macht, dass Jest nach .ts
und .tsx
Dateien schaut. Das transform
sagt, dass Jest jegliche TypeScript Dateien durch den ts-jest
Präprozessor zum laufen bringt, der sie über den TypeScript-Compiler ausführt und JavaScript erzeugt, das Jest erkennt. Dann schließlich habe ich die testRegex
Einstellung geupdatet, um nach *.spec.ts(x)
Dateien zu schauen (meine bevorzugte Namensgebungkonvention für Tests).
Damit kann ich einfach jest
ausführen und alles läuft wie erwartet.
Linting mit TSLint
Obwohl TypeScript deinen Code auf viele Arten checkt, wollte ich trotzdem noch einen Linter, um einige weitere Code-Style- und Qualitätschecks zu ermöglichen. Ähnlich wie ESLint in JavaScript ist TSLint die beste Option, um TypeScript Dateien zu überprüfen. Es funktioniert auf die gleiche Art wie ESLint – mit einer Reihe von Regeln die du aktivierst oder deaktivierst. Und es gibt auch ein TSLint-React Paket, mit dem du spezifische Regeln hinzufügen kannst.
Du kannst TSLint über eine tslint.json
Datei konfigurieren. Meine ist unten. Ich benutze beide, die tslint:latest
und die tslint-react
Presets, welche eine Reihe von Regeln ermöglichen. Ich bin jedoch mit einigen der Defaults nicht einverstanden, also überschreibe ich sie – du kannst das aber auch anders regeln – das liegt bei dir!
{
"defaultSeverity": "error",
"extends": ["tslint:latest", "tslint-react"],
"jsRules": {},
"rules": {
// use single quotes, but enforce double quotes in JSX
"quotemark": [true, "single", "jsx-double"],
// I prefer no semi colons :)
"semicolon": [true, "never"],
// This rule makes each Interface be prefixed with 'I' which I don't like
"interface-name": [true, "never-prefix"],
// This rule enforces objects to always have keys in alphabetical order
"object-literal-sort-keys": false
},
"rulesDirectory": []
}
Dann kann ich das tslint --project tsconfig.json
laufen lassen, um mein Projekt zu linten.
Credits
Original gepostet von Jack Franklin.
Inhaltsverzeichnis
Um alle Neuigkeiten zu erfahren, abonniere hier unseren Newsletter!
Newsletter abonnieren