Closed spalladino closed 5 years ago
This document outlines my proposed general plan for migrating ZeppelinOS to Typescript. The steps don't need to be followed to the letter, but they lay out a tempting path to use as a backbone for the migration.
This plan focuses on the complete port of the lib
and cli
packages. The docs
and vouching
packages are left out of the migration for now. The test folders within each package are left out, but some tests may be ported for future reference.
This comment lays out the plan for porting the lib package. A future comment will lay out the plan for migrating cli, using this plan as a reference.
Files that have less dependencies should be ported first. This defines a porting tree that is traversed from leaf to branch, to the main trunk of the tree. To help identify and visualize the dependency tree Webstorm's UML diagrams feature can be used, as well as the madge NPM package, which can be used as follows:
madge packages/lib/src --exclude ../lib --summary
Having said that, the priority is to port public facing elements first. I.e. public interfaces are more important than encapsulated private members. This will allow us to work on external type declarations as we port the packages.
In order to keep PRs in a size that is reviewable, files should be ported in batches of 3-4, depending on complexity and submitted for review via a pull request. Before pushing your changes, make sure tests in lib and cli pass locally, and then make sure that the CI tests pass after pushing the changes.
As we port the code, we may realize things that need to be done but can't be done at the moment, or fall outside of the scope of the current tast. In these cases, add comments with the following marker:
// TS-TODO: Your comment here.
For now, we are using TSLint's "recommended settings". If you see something that bothers you, go to root/tslint.json and edit the file. If someone has already created a setting and you are about to change it, consider leaving a note for discussion in slack. We will revisit the linter settings in phase 5. If you definitely want to change the setting now, you can simply adjust it, then run:
npm run lint
to verify the files that become affected by the rule change, and
npm run lintfix
to implement the new rule in the code.
For now, just type as you see it naturally and don't worry too much about conventions. We will revisit architecture design decisions in phase 6. The idea in the first stages is to move away from Js in any reasonable way. Once we hace Ts syntax, typed dependencies, strict compiler settings, we will have much more info to make architectural design decisions. For now, don't be afraid to use any
types, but if you see a structure, either local or from a dependency, that could have a complex type, just leave a TS-TODO
comment.
There don't seem to be good tools around for automating at least part of the process of converting a Javascript file to Typescript. However, simply renaming the file the right way, and using TSLint to make some style changes on the file, before actually starting to type it is very helpful. This PR contains a script to save time with this, which needs to be run on single files, without extension:
./scripts/ts_prepare.sh src/utils/ABIs
The script will list the changes that will be made to the file and prompt the user for confirmation. After the changes are applied, you can jump into the file and manually add types without wasting time with style related stuff.
The migration is divided into the following set of phases. Tasks within a phase can be parallelized, but a new phase should not be started until the previous one is completed.
This phase involves bootstrapping Typescript into the project, so that the migration process can begin.
tslint.json
.This phases involves porting all .js
files to .ts
files contained within the src
folder of packages/lib
, following the suggested file port order from the guidelines above. Note that in this phase we are not paying particular attention to OOP design patterns, but merely porting the Javascript syntax to Typescript syntax. This means that the port involves applying types as much as possible, but not worrying about perfect typing of all local objects, or using perfect typings for 3rd party dependencies, which will be addressed in following phases.
At the end of this phase, there should be no .js
files in packages/lib/src
. The following task list shows all files, ordered by number of dependencies.
As soon as packages/lib
fully uses types, we should generate declaration .d.ts
files so that the package can be used by packages/cli
with types.
declaration: true
, allowJs: false
).After Truffle has been removed from the testing pipeline, the same process applied to the src
folders in phase 1 can be applied to the test
folders. However, we only need some of the tests to be ported in the lib package, so that we can use it for future reference in porting the rest of the tests.
This phase involves introducing typings to all the 3rd party dependencies used in the project. More precisely, this involves using Typescript 2.x's @types dependencies so that the compiler understands the types used by the dependencies, instead of it inferring any
all over the place. This also involves adapting our code to use these types.
Important: We should also consider replacing existing dependencies with pure Typescript packages, especially for web3 related stuff.
[ ] Consider using https://github.com/ethereum-ts/TypeChain - Here's an experiment => https://github.com/zeppelinos/zos/pull/511
[ ] Consider using types for AST output https://github.com/kodebox-io/language-solidity
[x] packages/lib
This phase involves enabling stricter Typescript compiler settings and making all the necessary changes in the Typescript files. The documentation for all compiler settings can be found at http://www.typescriptlang.org/docs/handbook/compiler-options.html. The settings should be set in the following order:
.js
files..js
files.this.
needs to be typed always.any
type.null
and undefined
are strict types, to be handled by unions.tslint-eslint-rules
: https://github.com/buzinas/tslint-eslint-rules In this phase, we can begin focusing on refactors that make the code more OOP designed. This means not just complying to Typescript syntax, but actually architecturing the code around Classes, Interfases, Generics, etc. That is, making the most out of the OOP design patterns that Typescript enables.
*TBD: Define task list of refactors that enforce a more OOP-like architecture. Drawing an in depth UML diagram would help in identifying OOP structures that could be applied.
TS-TODO
comments.@ajsantander looks great! A few comments:
@spalladino I've updated my previous comment (the plan) with your suggestions. Thank you =D
This is the continuation of the migration plan, now focused on the cli package. Please use the migration plan for lib as reference.
Idem Phase 1 for lib, but for packages/cli
.
Idem phase 2 for lib, but for the cli package.
declaration: true
, allowJs: false
).This phase involves enabling stricter Typescript compiler settings and making all the necessary changes in the Typescript files. The documentation for all compiler settings can be found at http://www.typescriptlang.org/docs/handbook/compiler-options.html. The settings should be set in the following order:
.js
files..js
files.this.
needs to be typed always.any
type.null
and undefined
are strict types, to be handled by unions.In this phase, we can begin focusing on refactors that make the code more OOP designed. This means not just complying to Typescript syntax, but actually architecturing the code around Classes, Interfases, Generics, etc. That is, making the most out of the OOP design patterns that Typescript enables.
*TBD: Define task list of refactors that enforce a more OOP-like architecture. Drawing an in depth UML diagram would help in identifying OOP structures that could be applied.
TS-TODO
comments.At this point, most of Lib and Cli should be ported to Typescript. Next steps, which could be turned into independent issues, could be:
Closing this issue, the migration plan was approved, @ajsantander please create small issues splitting the plan above so we can keep track of each step. Great job!
Consider the following tasks: