8 ms·
Migrating a 10,000-line legacy JavaScript codebase to TypeScript
- ghh 10y agoThis is kind of a random Typescript tip, but when migrating regular Javascript, there is an alternative to adding the type 'any' to every object to 'silence' the compiler. That is to introduce a preliminary type definition. Instead of: const oldVar: any = { field: 1, ... } function foo(bar: any) { ... } You can write: declare type OldVarType = any const oldVar: OldVarType = { field: 1, ... } function foo(bar: OldVarType) { ... } This way, you can signal that it's not just any kind of any, but a particular kind of any, which is now trackable in your codebase. When you're ready, you can gradually update the OldVarType declaration and solve the compiler type check warnings from there. Union types [1] can be quite useful then too. [1] https://www.typescriptlang.org/docs/handbook/advanced-types.html https://www.typescriptlang.org/docs/handbook/advanced-types....
- peteretep 10y agoAh, that's a neat idea. I wonder if there's any easy way to then scan your code for type aliases that are `any` to make sure you've caught all of those.
- wvenable 10y agoProbably best to put all the any type aliases in one place then you know what they are all.
- ghh 10y agoMost development setups have a way to do a regex search through a codebase; You'd be looking for declare type.*any
- wrigby 10y agoIf you're IDE doesn't do it, then I highly recommend using ack[1]. It's my go-to tool for tasks like this, and I've found it a joy to use. 1: http://beyondgrep.com http://beyondgrep.com
- wldcordeiro 10y agoCommon prefix or suffix? I know in the RxJS community it's not uncommon to suffix an Observable variable like `fooBar$`
- smt88 10y agoI think that would be more trouble than it's worth. Since "any" is a keyword, you wouldn't even need regex to search for it. Something like this would work: " = any" This would only show types that are aliases of the "any" type. As a side note, the following would create a compiler error ("cannot find name 'any'") because types are metadata and can't be used as program data. const something = any;
- Sharlin 10y agoHeh, sure does bring memories of the C64 BASIC where string-typed variables and functions were marked with a $ suffix :)
- chrismbarr 10y agoYes! We have several 100% TypeScript applications and we've found TSLint to be invaluable for maintaining code quality - http://palantir.github.io/tslint/ http://palantir.github.io/tslint/ You can ban the `any` type along with lots of other rules. You can also write your own custom rules, or implimnet rulesets by 3rd parties like Microsoft: https://github.com/Microsoft/tslint-microsoft-contrib https://github.com/Microsoft/tslint-microsoft-contrib and ESLint: https://github.com/buzinas/tslint-eslint-rules https://github.com/buzinas/tslint-eslint-rules
- jsprogrammer 10y agoShouldn't my compiler do such things for me?
- ludamad 10y agoWhat do you want the compiler to do in this scenario?
- jsprogrammer 10y agoInfer and track types. Just as was described in the parent post.
- ehsanu1 10y agoBoth those versions should work the same from the compiler's point of view. But from a programmer's point of view, specifically someone migrating a JS system to TS, it's useful to read "oldVarType" instead of "any", because it tells you that it was just a quick way to migrate rather than necessarily designed to be "any".
- jsprogrammer 10y agoYes, and it would be nice if the compiler could produce such an annotated version automatically, instead of the programmer having to use the highly error-prone technique of typing it all out manually. For a single function it's not a bug deal, but if you are migrating 10k lines, I'd view it as a near necessity.
- jdmichal 10y agoSo you want the compiler to automatically generate alias types for every un-typed variable and parameter? I assume you would also want the compiler to (somehow?) infer types that should be the same type and merge them, since otherwise the first step is kind of silly. I'd argue that that's a pretty difficult task, tantamount to inferring programmer intent.
- rkwz 10y agoI curious how using Typescript makes the project more "structured" compared to using plain JS. Typescript just gives you static types and classes. I can understand "classes" make the code more modular and thereby more structured, but can't you already do that using ES5 "class"? Types help in catching errors early and can help in documenting the interface of the methods etc. Also, if the other problem was isolating the scope of modules and dependency injection, it could be easily done using Requirejs and all the modules could be concatenated during build time. Webpack is a cool tool, but given the nature of its documentation, the maintenance would be really costly in the long run. Requirejs is just more established and satisfies all the author's requirements. It would've been really helpful if the author had included additional reasoning behind the choices made.
- WayneBro 10y agoThe types are what give it structure. Types are the structure. When you create a type, you are saying in concrete terms "this is exactly how things happen and nothing else". This is very useful information for a large project. Think of it like a map. You don't need one for your house, but you need one to walk cross country. Or like NASA procedural manuals for something normal people don't need a guide for...like an astronaut taking a shower or something.
- rkwz 10y agoI don't understand. > Types are the structure. When you create a type, you are saying in concrete terms "this is exactly how things happen and nothing else". Types make the interface concrete - which is better for compiler/interpreter (catch bugs) and better for developers (easy to understand instead of guesswork) But when I think about "structure", It is usually about how the codebase is organized. How different modules interact with each other and is the flow of the code easily understandable. This could be easily achieved without Types. Maybe it's just my interpretation of the term "structure".
- pixie_ 10y agoIt is pretty much impossible to have any sort of inferred structure from plain javascript. The reason you can use super powerful tools to refactor, rename, and find all references across files in typescript is because the IDE can build a 100% accurate data model of your code base when it's written in typescript.
- AnkhMorporkian 10y agoI've always wondered if you could create a runtime inferrer of types for these sorts of projects. As in, attach something that observes function calls and sees over the course of some amount of time what types are passed into them, and have them make a best guess of what arguments are what types. While of course it couldn't be perfect, it could save a lot of grunt work. I made a quick version of this idea in Python 3.4, where it was much easier since I could modify the AST on import, but I never quite got it where I wanted it and sort of lost passion.
- honua 10y agoI was wanting the same kind of thing! We have an old Node (lol, funny to write that) code base of about 60 services that fetch data from 3rd party APIs. They're all very dis-organized and written in different styles, so a tool like that would be really useful. I've been experimenting with using Facebook's Flow on this, but it doesn't do the magic that you describe (which I'd like)
- smt88 10y agoThe good thing about TS is that you can add types gradually. I actually think there's a lot of value in adding the typing manually because A) you're guaranteed to find bugs, and B) you have to revisit your assumptions with the advantage of hindsight.
- clarus 10y agoWith Flow you can also add types gradually. Actually, one of the reasons why Facebook created Flow is that they thought that TypeScript was not "gradual enough". A good presentation on how Flow differs from other type systems: https://www.youtube.com/watch?v=VEaDsKyDxkY https://www.youtube.com/watch?v=VEaDsKyDxkY
- patates 10y agoI also wonder why no one experimented on exporting the types from jitters and making preliminary type annotations from that information. Sounds very basic when I think about it but I have no idea how jitters work so maybe it's just nonsense.
- Dolores12 10y agoTLDR: rename files to .ts, download type definitions for external libraries, silence TypeScript compiler warnings using 'any'
- chrismbarr 10y agoMaybe initially, but that `any` type really defeats the purpose of having types in the first place. Where I work we have several 100% TS apps and we've banned the `any` type except for very rare exceptions. It's always better to define a type so that it can be easily used elsewhere, and it also helps prevent typos.
- k__ 10y agofor data objects (no methods) you could at least use Object instead of any, this should prevent anyone from calling methods on it.
- nudpiedo 10y agoBy using haxe he could have started now transpiling some of those 10k lines also to the python backend just for free plus all the additional prepocessor and type checks that are common for typescript and haxe. I don't really understand why people jumps to typescript and not haxe, which in my opinion has more strategic advantages and warranties (with very few exceptions). Is perhaps just a marketing topic?
- joneil 10y agoI've used Haxe in several production projects, and if I'm starting a new code-base from scratch I would pick it again. (I prefer type system, the cross-compilation options, and the meta programming) But if you're migrating an existing code base, TypeScript has a more obvious path forward. Because TypeScript is a superset of JS, it means that all of the JS files in your project will be compiled by TypeScript as is. And you can add the extra typing information as you go, it doesn't have to be an "all or nothing" transition. Compare to Haxe, where you have to create a separate *.hx file, with a slightly different syntax. You can either translate all of your code (a massive undertaking), or type external files as an `extern Class`, or just use `Dynamic` (equivalent of TypeScript's "any"). But all of these involve a little bit of work for every single part of your code base, so it's going to add up.
- nudpiedo 10y agoFrom what you say most of those problems are just syntactical, isn't it? Somehow I started to believe that it is a matter of visibility and self selling skills in the haxe community. The problems you mention seems to be possible to solve with software (AST generation and transformation) however it still has to be done. Let's hope we will see soon a project like that in haxe-land.
- joneil 10y agoYou are right that the difference is syntax, but the thing is, JS syntax will always be different from Haxe syntax. You'll never be able to use "jQuery.js" as valid Haxe code, but it is valid TypeScript code. There might be some interesting possibilities. For example back2dos has an example of Haxe seamlessly integrating with external ".as" files on the Flash target. This could be conceivable for other strict languages like Java or C# too. You might even be able to seamlessly integrate with ".ts" files, because they have some typing information. (That would be a fun project!) But JS dynamic typing can be pretty crazy, and I'm not sure how feasible it is to be able to infer type information from JS source code without special type hints. It would end up just calling everything "Dynamic", and then you're getting no benefit.
- kevan 10y agoI'm about halfway through migrating a ~30kloc ES5 codebase to ES6 with a transpiler (Babel), linter (ESLint), and fast test suite (Mocha) [1]. Once all files are going through the transpiler I'll probably add TypeScript or Flow. We already had RequireJS in place so the conversion from AMD to ES6 module format has been pretty straightforward, but I'm continually amazed with every file I convert I usually find a couple real bugs with the linter. The most common have been: * Undefined variables referenced in a seldom-traveled conditional branch. * Leaked globals due to missing `var` keyword. Every bug I find is immediate feedback that this is a worthwhile use of our time. [1] https://medium.com/@kevanahlquist/evolution-of-javascript-at-bluestem-285e4fc5ca26#.f9mn885tm https://medium.com/@kevanahlquist/evolution-of-javascript-at...
- muglug 10y agoI can't recommend flow highly enough for gradually typing existing code (as it requires far fewer code changes than TypeScript). Not sure what your timelines are like, but it takes no time at all to set flow up, and you might find it makes the task of migrating a little more varied.
- smt88 10y agoI disagree. In terms of changing code, TypeScript and Flow are similar (you're just gradually adding metadata). The differences are in tooling.
- clarus 10y agoI think the main differences are in the type engines. From what I have seen, Flow can infer much more informations about a program, even on unannotated JavaScript. As a result, with the same amount of annotations, it seems to me that you can find more bugs using Flow than using TypeScript.
- nojvek 10y agoI find typescript pays off with the excellent intellisense and refactoring in editor support. They did a great job of having the language service and compiler use same core code base.
- partycoder 10y agoA machine is faster and more reliable than a human at comparing. Matching types for verification is one of those things.
- deleted 10y ago[deleted]
- mark_l_watson 10y agoNice article, and I am going to give webpack a try. I took an eDX class in Typescript a year ago and really like the language but decided modern JavaScript was getting "good enough." I regret that decision sometimes because Tyoescript really is better IMHO.
- CuriouslyC 10y agoTypescript is ridiculously easy to learn. The only hard part is getting your typings/build/environment set up, the actual semantics of the language are a breeze. There are some subtle nuances to structural typing and interface definitions if you want to write really slick code, but for the most part you can get by without them.
- nanoojaboo 10y agoMr PGBovine, I liked your cpython video series
- deleted 10y ago[deleted]
- mizzao 10y agoI feel that academic code is generally more prone to bad-smell pressure than industry code, since people generally produce for papers and deadlines and "just get it working for now." It's good to see that it's possible to produce some semblance of order in the constant grind.
- balls187 10y ago> Since globals defined in different JavaScript files share a common namespace, I often found it convenient to reference globals...I knew all along that these were bad habits This line resonated with me. In my first CS course (Intro to programming), I learned about why using globals can be problematic. A good reminder that engineering is all about trade-offs. Sacrifice future maintainability for present day productivity.
- alphaomegacode 10y agoLots of intelligent replies here (like much of HN); having a Java & C/C++ background, I picked up Javascript some yrs ago for web work. Now, I don't know how/if to move to Typescript or ES6? And any resources/books/vids you all highly recommend? (I built some corp stuff in Angular 1 but guess I'll have to move to TS for Ang 2 or learn React?) Many commenters in this thread seem experienced in TS so thanks in advance for sharing any advice