In the fourth and last part of this series about how to program faster I’ll show you other ways of writing less code, i. e. Including-mechanism and using another language to avoid doing things over and over.
If you haven’t read the other articles of this series, here is a quick overview:
Part 1: I give some sort of theoretical introduction into what I mean by writing less code and why that is a good idea (e. g. maintainability, readability).
Part 2: I show you how you can apply destructuring, use the arrow function and default parameters to reduce code.
Part 3: You learn how Magic Getter/Setter (ES6) works and when and how to use Variable Function Names
Now, let’s dig into including-mechanisms.
Re-Exporting – index.(js|ts) for each directory (TypeScript, ES6)
The examples I gave in part 2 and part 3 were all about code, now we talk about organizing your code with the goal in mind: writing shorter code.
First, how you should not do it. It’s some code I wrote in TypeScript.
import { ConnectedComponent as Overview } from './overview/OverviewComponent'; import { columnDefinitions as overviewColumnDefinitions } from './overview/OverviewColumnDefinition'; import { ConnectedComponent as Needsaction } from './needsaction/NeedsactionComponent'; import { columnDefinitions as needsactionColumnDefinitions } from './needsaction/NeedsactionColumnDefinition'; ... and so on 5 more entries ...
I have to admit, now I shudder to look at this piece of work. Why? It’s long and unreadable. And ugly. But it works.
It was one of my first TypeScript programs. I was happy that it worked and didn’t care much about readability. Now, fixing this is not easy because I need to rename the imports in the components to make things more clear.
But ES6 allows defining an index for each directory. In the same project, I misused that index for some kind of „default page“. It reminded me of an „index.html“, a file that an HTTP-server delivers when specified no resource, and so I used it as that. Well, it works similar but this has more uses. Like: what if the index is used to create … an index of exported code in this directory?
Interesting idea: Let’s try to make an index.ts (the same works in ES6 identical for index.js). For this example in the overview/ and needsaction/-directory:
export { ConnectedComponent as Overview } from './overview/OverviewComponent'; export { columnDefinitions as overviewColumnDefinitions } from './overview/OverviewColumnDefinition';
export { ConnectedComponent as Needsaction } from './needsaction/NeedsactionComponent'; export { columnDefinitions as needsactionColumnDefinitions } from './needsaction/NeedsactionColumnDefinition';
And now I could write
import { Overview, overviewColumnDefinitions } from './overview'; import { Needsaction, needsactionColumnDefinitions } from './needsaction';
It’s much more readable, shorter and still clear on what it does.
Why should I need to know the exact filenames when I read the code? You used the index for the mappings so that you have independent components now. Similar names for similar things!
This has two disadvantages, however:
- It disables tree-shaking. That is when the builder attempts to add only the imported files to the build. Webpack, for example, will not follow the re-exports and so it will import all files of the index. Small projects can live with the extra shipped code; it turns back in time you need to read/understand that code without such simpler index. And in larger projects, you need to think deeper about this kind of problem. As a rule: there are more factors that blow up your code than this.
- The IDE silently imports things, that are not indexed (yet). Either all (own) imports are from the indexes, or from the files, but avoid mixing the styles. (Turn off automatic imports or place an empty package.json in each directory.)
This is something which needs to be thought through before you introduce it into your project – as with all the tips in this article!
Sidetrack: A Typed Language Helps to Keep Track over Complexity
Talking about TypeScript: it’s badass compared to ES6 in terms of destructuring. Why? If you use TypeScript you can be sure that you don’t make stupid mistakes. The compiler knows exactly which paths the destructurized data goes. BTW: switching an ES6 project to TypeScript is manageable.
Let me show you a simple example where you can see the advantage: searching the forgotten comma.
function splitIntoTwoParts(ins) { return ins.split(' ', 2); } const [,part1, part2 ] = splitIntoTwoParts('1 2'); // ^ // forgot to remove the comma here!!
> part1 2 // huh? > part2 undefined // meeeeeeeee!
The prepended comma is valid – it’s called Elision – so this will fail only at runtime. Much fun to find such errors in a larger project. Please avoid, however, multipart-return-values in ES6, it’s a code smell. In typed languages like TypeScript this cannot happen:
function splitIntoTwoParts(ins: string): [ string, string ] { // we need to map the returned array into a tuple (not the most elegant way here, but quite readable) const out = ins.split(' ', 2); return [ out[0], out[1] ]; } const [,part1, part2 ] = splitIntoTwoParts('1 2'); // same mistake
> tsc splittest.ts splittest.ts(6,16): error TS2493: Tuple type '[string, string]' with length '2' cannot be assigned to tuple with length '3'.
You surely will find the bug then within minutes – not hours.
As you can see destructuring (which I explained in-depth in part 1) becomes much more powerful in TypeScript. It makes sure that you cannot make a fool-mix of your mappings. Typed languages avoid such errors and enable the use of more complex code in a safe way.
Property Initialization and Constructor Assignments – A Shorthand for Constructor-Parameters (TypeScript only!)
Another example where TypeScript makes things a lot easier: initialization of classes
class InitInConstructor { public init: string; constructor() { this.init = 'initial value'; } } const a = new InitInConstructor(); console.log(a.init); // outputs "initial value"
This is equal:
class InitInDefinition { public init: string = 'initial value'; } const b = new InitInDefinition(); console.log(b.init) // outputs "initial value"
This kind of initialization also works outside of classes. But what if you want to initialize not with a constant value? Like this:
class Example1 { private _parameter1: number; private _parameter2: string; constructor (parameter1: number, parameter2: string) { this._parameter1 = parameter1; this._parameter2 = parameter2; } }
I can write that as
class Example2 { constructor (private _parameter1: number, private _parameter2: string); }
Both examples have identical functionality. I find it quite useful as I need it frequently.
Another code-example (as you can see, it also works with public variables):
class Example3 { constructor(doingItWrong: string, private aPrivateVar: string, public aPublicVar) { } logIt() { console.log(this.doingItWrong) // Compiler error: Property 'doingItWrong' does not exist on type 'test'. console.log(this.aPrivateVar); console.log(this.aPublicVar); } } const tester = new Example3('One', 'Two', 'Three'); tester.testMethod(); console.log(tester.doingItWrong); // Compiler error: Property 'doingItWrong' does not exist on type 'test'. console.log(tester.aPrivateVar); // Compiler error: 'aPrivateVar' is private and only accessible within class 'test'. console.log(tester.aPublicVar); // Publics are accessible from outside of class, as used to
Summary
As may have noticed in this article we have left the scope of JavaScript (ES6), this where creativity begins. All presented methods aim to make your code smaller. There is much more, I could not mention, but JavaScript is a fast-changing language. It wasn’t my target to show a complete list.
To wrap it up
Smaller code is generally easier to maintain. One reason for that is that you can read it faster.
However, less code is always a double-edged sword. Pro: any line of code that you spare will exponentially repay over time. Well, exponential in theory, in practice shorter code seems at first more „magic“. The con side: you need more knowledge/experience to read it fast enough.
Hope you learned some ways to reduce code. Let me know what you think or share what you use to reduce code.
Schreibe einen Kommentar