Πώς να προσθέσετε TypeScript σε ένα έργο JavaScript

Μου αρέσει να γράφω κώδικα. Και θέλω να είμαι πολύ καλός σε αυτό. Αλλά κατά κάποιον τρόπο, το να γράφω JavaScript δεν ήταν ποτέ το δικό μου.

Ανεξάρτητα από το πόσο έχω ασκήσει, τα ίδια λάθη συνέχισαν να εμφανίζονται στην παραγωγή: cannot read property of undefinedεξαιρέσεις, η περίφημη [Object object]συμβολοσειρά και ακόμη και κλήσεις λειτουργίας με μη έγκυρο αριθμό παραμέτρων.

Επιπλέον, οι περισσότερες από τις βάσεις κώδικα που δούλευα ήταν πολύ μεγάλες JavaScript. Λοιπόν, εδώ είναι ένα ωραίο διάγραμμα για το πώς ένιωσα να είμαι εγώ:

Σε αυτήν την ανάρτηση, θα αποφύγω να εξηγήσω γιατί το TypeScript είναι φοβερό (και είναι) και θα επικεντρωθώ στις εργασίες που πρέπει να ολοκληρώσετε εάν θέλετε να μεταφέρετε το έργο JavaScript βανίλιας σε ένα μικτό έργο TypeScript.

Μέχρι το τέλος της ανάρτησης, θα είστε πιο ευτυχισμένοι και θα μπορείτε να απαντήσετε στις ακόλουθες ερωτήσεις:

  • Πώς μπορώ να προσθέσω τύπους στο έργο JavaScript;
  • Τι είναι το TypeScript;
  • Πώς μπορώ να χρησιμοποιήσω το TypeScript σε ένα έργο JavaScript;
  • Ποια είναι τα βήματα για τη μετατροπή μιας εφαρμογής JavaScript σε υποστήριξη TypeScript;
  • Πώς μπορώ να φροντίσω την κατασκευή και τη συσκευασία;
  • Πώς μπορώ να φροντίσω το χνούδι;
  • Πώς μπορώ να "πουλήσω" TypeScript στον οργανισμό και στους προγραμματιστές μου;

Πώς μπορώ να προσθέσω τύπους στο έργο JavaScript;

Το Vanilla JavaScript δεν υποστηρίζει τύπους αυτή τη στιγμή, επομένως χρειαζόμαστε κάποιο είδος αφαίρεσης πάνω από το JavaScript για να το κάνουμε.

Μερικά κοινά αφαιρέσεις τη χρήση στατικού τύπου πούλι του Facebook που ονομάζεται flowκαι η γλώσσα της Microsoft που ονομάζεται: typescript.

Αυτή η ανάρτηση ιστολογίου θα εξετάσει τη χρήση και την προσθήκη TypeScript στο έργο σας JavaScript.

Τι είναι το Typescript;

Το TypeScript είναι ένα τυπικό υπερσύνολο JavaScript που μεταγλωττίζεται σε απλή JavaScript.

Το TypeScript αποτελείται από μερικά μέρη. Η πρώτη είναι η γλώσσα TypeScript - αυτή είναι μια νέα γλώσσα που περιέχει όλες τις δυνατότητες JavaScript. Δείτε τις προδιαγραφές για περισσότερες πληροφορίες.

Ο δεύτερος είναι ο μεταγλωττιστής TypeScript, tsc(η μηχανή συστήματος τύπου) που είναι μια μηχανή συλλογής που δημιουργεί αρχεία ts και αποδίδει αρχεία js.

Γεια σας κόσμο σε TypeScript

Για παράδειγμα, αυτά είναι τα βήματα που πρέπει να ακολουθήσετε για να γράψετε την πρώτη σας εφαρμογή TypeScript:

  1. εγκαταστήστε TypeScript με npm i typescript
  2. δημιουργήστε έναν φάκελο που ονομάζεται exampleκαι cd σε αυτόν (στο τερματικό σας)
  3. δημιουργήστε ένα αρχείο που ονομάζεται hello.world.ts
  4. γράψτε τον παρακάτω κωδικό:
const firstWords:string = "hello world" console.info(firstWords); 

και μετά αποθηκεύστε το.

5. Εκτελέστε την tscεντολή για εκτέλεση του μεταγλωττιστή TypeScript στον τρέχοντα φάκελο

6. παρατηρήστε ότι έχετε ένα hello.jsαρχείο που μπορείτε να εκτελέσετε τώρα :)

7. τρέξτε node ./hello.js

Πώς μπορώ να χρησιμοποιήσω το TypeScript σε ένα έργο JavaScript;

Υπάρχουν μερικές στρατηγικές για την πραγματοποίηση αυτής της «μετανάστευσης» (από εταιρική άποψη και από κώδικα). Τα έχω παραθέσει παρακάτω με το "κόστος" τους και από την αξία που παρέχουν.

Θα πρότεινα να ξεκινήσετε με την "υποστήριξη TS εφαρμογής" και να προχωρήσετε αφού αποδείξετε την αξία στην ομάδα ανάπτυξης.

Η προσέγγιση "μικρό βήμα για τον άνθρωπο" - Προσθήκη υποστήριξης TS για υπάρχουσες εφαρμογές

Η πρώτη μου πρόταση είναι να δημιουργήσω ένα μείγμα των δύο γλωσσών σε ένα ενιαίο έργο και μετά να γράψω όλο τον "μελλοντικό" κώδικα στο TypeScript.

Ο συνδυασμός δύο γλωσσών σε ένα μεμονωμένο έργο ακούγεται αρκετά απαίσιος στην αρχή, αλλά λειτουργεί αρκετά καλά αφού το TS δημιουργήθηκε για σταδιακή χρήση. Αρχικά μπορεί να χρησιμοποιηθεί όπως το JS με αρχεία .ts και περίεργες γραμμές εισαγωγής.

Σε αυτήν τη στρατηγική, θα συντάξουμε τα αρχεία TypeScript που έχουν μεταναστεύσει και θα αντιγράψουμε απλά τα αρχεία JavaScript σε ένα φάκελο εξόδου.

Το τεράστιο όφελος αυτής της προσέγγισης είναι ότι επιτρέπει μια σταδιακή καμπύλη μάθησης για την ομάδα ανάπτυξης (και για εσάς) με τη γλώσσα και τα χαρακτηριστικά της. Σας δίνει επίσης πρακτική εμπειρία και εικόνα για τα υπέρ και τα κατά.

Συνιστώ ανεπιφύλακτα να ξεκινήσετε από αυτό το βήμα και μετά να το επαναλάβετε με την ομάδα σας προτού προχωρήσετε. Για ένα γρήγορο "πώς να το κάνετε αυτό", μετακινηθείτε προς τα κάτω για να The steps to convert a javascript application to support typescriptχωρίσετε.

Το άνοιγμα για επιχειρηματική προσέγγιση - Προσθήκη υποστήριξης TS για υπάρχουσες βιβλιοθήκες.

Αφού έχετε εμπειρία στην TS και η ομάδα ανάπτυξης σας συμφωνήσει ότι αξίζει να προχωρήσετε, προτείνω να μετατρέψετε τις εσωτερικές βιβλιοθήκες και τις ενότητες σας για να υποστηρίξετε την TS

Αυτό μπορεί να γίνει με δύο τρόπους:

The first way involves using declaration files. A simple addition of d.ts files helps the TS compiler type-check existing JavaScript code and gives you auto-completion support in your IDE.

This is the "cheapest" option, as it doesn't require any code changes to the library at all. It also gives you maximum power and types support in your future code.

The second way is to perform a full re-write of TypeScript, which might be time-consuming and error-prone. I would advise against it, unless it proves ROI worthy to your team.

The skeleton - a step towards the future

I assume most developers are "lazy" and usually start their application by copying from a skeleton (which usually contains logging, metrics, configuration, and so on).

This step helps you navigate your way into a bright future, by creating an "official" skeleton for your company. It will be 100% TS, and deprecates the old JS skeleton if one exists.

This typescript-node-starter is a really good first project to start with.

The all in approach - Converting a full codebase from JS into TS

This option requires a total rewrite from JavaScript code to TypeScript.

I would recommend doing this as a final step in the TS migration process since it requires a total application re-write and deep knowledge of TypeScript and it's features.

You can do such a rewrite (it's a long process) in the following manner:

  1. Define clear types for your application business logic, API, & HTTP's
  2. Use @types packages for all the libraries in your package.json. Most of the libraries out there support TS, and in this process I suggest migrating them one by one (by just adding @types/ in your package.json file).
  3. Convert your application logical components in order of their importance. The more unique the business logic, the better.
  4. Convert the IO parts of your application, database layers, queues and so on.
  5. Convert your tests.

Keep in mind that there are automated tools designed to ease this process, for example ts-migrate from the Airbnb team.

It tackles this problem from a different perspective, and converts all files to TypeScript. It also allows gradual improvements (like mentioned in the steps above) while the entire codebase is TypeScript from day one.

How to convert a JavaScript application to support TypeScript.

Install typescript

by running : npm install typescript.

Typescript config file

Add a typescript config file, which can be created using the tsc --init command in you CLI.

Here is an example of how our initial config looked:

{ "compilerOptions": { "target": "esnext", "module": "commonjs", "allowJs": true, "checkJs": false, "outDir": "dist", "rootDir": ".", "strict": false, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ "declaration": true, /* Generates corresponding '.d.ts' file. */ "strictNullChecks": true, "resolveJsonModule": true, "sourceMap": true, "baseUrl": ".", "paths": { "*": [ "*", "src/*", "src/setup/*", "src/logic/*", "src/models/*", "config/*" ] }, }, "exclude": ["node_modules", "dist"], "include": [ "./src", "./test", "./*", "./config" ] }

A few things to notice above:

  • We read all the files in the src or test or config directory (using the include flag).
  • We accept JavaScript files as inputs (using the allowJs flag).
  • We emit all of the output files in build (using the outDirflag).

Create your first .TS file in your project

I recommend starting by adding a simple TypeScript file (or changing a really simple JS file to a TS one) and deploying. Take this migration one step at a time.

Take care of your package.json file

Here is how our package.json looks before and after:

{ "scripts": { "start": "node ./application.js", "mocha": "mocha --recursive --reporter spec -r test/bootstrap.js", "test": "npm run mocha -- test/ -r test/integration/bootstrap.js", } }
{ "scripts": { "start": "node ./dist/application.js", "build-dist": "./node_modules/typescript/bin/tsc", "mocha": "mocha --recursive --reporter spec -r ./dist/test/bootstrap.js", "test": "npm run mocha -- ./dist/test/ -r ./dist/test/integration/bootstrap.js" } }

As you can see, most of the changes were about adding the prefix dist to most of our build commands. We also added a build-dist script that compiles our codebase and moves all files to a dedicated folder called dist.

Add source-map-support

One of the big issues when adding TypeScript to your project is that you are adding a layer of indirection between the code you write and the code that actually runs in production (since .ts is transpiled  to .js  in run time).

For example, imagine the following TypeScript program:

const errorMessage: string = "this is bad" throw new Error(a)

When we run it, it will throw the following stack-trace:

Error: this is bad at Object. (/Users/dorsev/work/git/example/hello.js:3:7)

This is problematic since our code-base contains only .ts files. And since most production code contains hundreds of lines, it will be really time-consuming translating these numbers and files properly.

Luckily for us, there is a solution for this called source-map-support!

This allows us to ensure that stack-traces will have proper .ts file names and line numbers like we are used to :)

This can be done by running npm install source-map-support and then adding the following line in the first lines of your application:

require('source-map-support').install();

The code now looks like this:

require('source-map-support').install(); const a:string = "this is bad" throw new Error(a)

And when we compile it we run tsc --sourcemap hello.ts. Now we get the following stack-trace which is awesome :)

Error: this is bad at Object. (/Users/dorsev/work/git/example/hello.ts:3:7)

In recent versions of nodejs, this is supported natively by using the --enable-source-maps flag.

How to take care of your build (Travis) & packaging

Let's just examine the before and after changes on our build configuration file.

This is how our .travis file looked before (simplified edition):

jobs: include: - &build-and-publish before_script: - npm install --no-optional --production - npm prune --production before_deploy: - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR} --exclude=.travis.yml --exclude=test -cJf "${ARTIFACTS_PATH}/${REPO_NAME}".tar.xz * .??* - &test before_script: - npm install --no-optional script: - echo "Running tests" - npm run lint && npm test

And this is how it looked after:

jobs: include: - &build-and-publish before_script: - npm install --no-optional --production - npm run build-dist # Build dist folder - npm prune --production before_deploy: - cp -rf config/env-templates ./dist/config/ - cp -rf node_modules ./dist/ - cd dist - XZ_OPT=-0 tar --exclude=.git --exclude=reports.xml --exclude=${ARTIFACTS_MAIN_DIR} --exclude=.travis.yml --exclude=test -cJf "${REPO_NAME}.tar.xz" * - mv ${REPO_NAME}.tar.xz "../${ARTIFACTS_PATH}" - cd .. - &test before_script: - npm install --no-optional - npm run build-dist script: - echo "Running tests" - npm run lint && npm test

Notice most changes concern "packaging" to the tar.xz file and running the build-dist command before accessing the dist folder.

How can I take care of linting?

There are a couple of linting solutions available.

The first solution we used was tsfmt  –  but then we decided against it later on because it requires you to maintain two separate configurations for your project (one for TypeScript using tsfmt and a separate one for JavaScript using eslint). The project also looks deprecated.

We then found TSLint  which redirected us to the eslint plugin for TypeScript. We then configured it as follows:

This was our eslintrc.js:

module.exports = { rules: { indent: [2, 2, { SwitchCase: 1 }], 'no-multi-spaces': 2, 'no-trailing-spaces': 2, 'space-before-blocks': 2, }, overrides: [{ files: ['**/*.ts'], parser: '@typescript-eslint/parser', plugins: ['@typescript-eslint'], extends: ['plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended'] }] }

Which we configured to run using a lint-fix command in our package.json which looks as follows:

{ "scripts": { "lint-fix": "node_modules/.bin/eslint . --fix" }, "pre-commit": ["lint-fix"] }

How to "sell" typescript to your development team

I believe one of the most critical aspects of introducing TypeScript to your organization is the "pitch" and how you present it to your development team.

Here is the presentation we presented internally which revolved around the following topics:

  1. Explain why we think TypeScript is awesome
  2. What is TypeScript
  3. Some basic code examples. The main point in this part is not to "teach" 100% TypeScript, since people will do that on their own. Instead, give people the feeling  that they can read and write TypeScript, and that the learning curve is not so hard.
  4. Advanced code examples, like Union types and Algebraic data-types which provide enormous values to a JS developer. This are a real treats, on top of typed-language and the compiler that will attract your developers to it.
  5. How to start using it. Encourage people to download the vs-code IDE and to add an annotation (//@ts-check) so they can start seeing the magic!  In our company, we prepared in advances some really cool mistakes that ts-check catches, and we did a live demo (2-3 minutes) to show how fast the TypeScript compiler can help them  using JS docs with type annotations or ts-check).
  6. Deep dive into some features. Explain ts.d files and @types packages which are some of the things you will encounter really early in your TypeScript codebases.
  7. Live PR's from your work. We showed the PR we created early on, and encouraged people to review it and try it out for themselves.
  8. Share some cool resources. There is a lot of content online, and it's hard to figure out good from bad. Do your teammates a solid and dig deeper and try to find quality content about the tools you use and need. Scroll down to the conclusion for my resources.
  9. Create a public pull request .  I recommend trying to get as much support as possible for its approval.

10.  Create a positive buzz in your organization about the change!

I highly recommend tweaking this list according to your team, standards, and time-constraints.

Conclusion

Typescript is super awesome! If you are writing production grade software and the business requirements and availability are high, I strongly encourage you to give typescript a try.

Just remember to take it one step at a time. New languages and frameworks are hard, so take the time to learn and to educate yourself and your team before pushing this process forward.

Create a short feedback loop and value proposition. It's hard to "sell" a new language to your team and management as it takes time and resources.

So design your migration process with short feedback loops, and try to define clear KPI's (fewer bugs in production, easier refactoring times, and so on) and make sure the value proposition for your use-case is constantly justified until it becomes the de-facto standard.  

Make learning resources readily available. I really enjoyed this talk about TypeScript first steps and this blog post about incremental migration to TypeScript.

Επίσης, μην χάσετε το denoέργο και το ts-nodeέργο. Είμαι πολύ ενθουσιασμένος και ανυπομονώ να τα χρησιμοποιήσω σύντομα.