DEV Community

Cover image for Writing a code analyzer in TypeScript (from scratch)

Writing a code analyzer in TypeScript (from scratch)

Derk-Jan Karrenbeld on June 10, 2019

Exercism is an online platform designed to help you improve your coding skills through practice and mentorship. Exercism provides you with thousan...
Collapse
 
jsjoeio profile image
Joe Previte (he/him)

I like that in your solution you say "Optimal is extracting the GIGASECOND_IN_MS value as a top-level constant".

I did not do this in my solution, but I can see how it makes your code more readable. I also like how you defined your variable name in all caps.

What's your personal rule of them for this? (i.e. when to use all caps)

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

Great questions!

I am actually writing the analyser code to approve_with_comment if someone has not extracted it. There are two variants we see that are approvable but not optimal.

// Variation one
export function gigasecond(...) {
 const gigasecond = 10 ** 12
 return ...
}

They already have it as a constant, but it's "re-created" each invocation. Extracting is might be a premature optimisation from a speed standpoint, but that's not it.

  1. The variable name is shadowing the function name, which leads to subtle bugs.

  2. Extracting the constant to the top-level allows for re-use (which is ok, not a necessity)

  3. Extracting the constant is declaring intent: this is a "compile"-time value, that is actually constant in value, and not just assignment, which is further solidified with making it UPPERCASE

In the above case, extracting it is a small gain, but mostly for declaring intent.

// Variation two
export function gigasecond(...) {
 return new Date(... + 10 ** 12)
}

There is nothing wrong with the solution above, but this is a magic number or rather magic expression. It's adding a number to date, but in a year's time, can you remember why it's 10 ** 12 and not 10 ** 9 (which would be giga)? Maintainability is easier if you name your constants.

What's your personal rule of them for this? (i.e. when to use all caps)

This is just a preference I inherited from other languages where this is enforced in the language (Ruby constants start with a Capital for example), but mostly I do this in JavaScript and TypeScript based on:

  • If the constant is constant per invocation, e.g. it is a computed constant that can change based on the state of the process or function (input is a state), then I use const camelCase.
  • If the constant is constant per process, e.g. it is the same value when "compiled" or when "first interpreted", it is probably a file-level (and thus process-level) constant. I use const UPPER_CASE

The added benefit of the approach above is:

  • It's instantly clear which VALUE is always the same and which functionValue depend on state.
  • Most code highlighters (such as prism, or the tsc in vscode etc.) will color the UPPER_CASE differently.

I hope these make sense!

Collapse
 
jsjoeio profile image
Joe Previte (he/him)

They already have it as a constant, but it's "re-created" each invocation.

Great point! Didn't even think about that when I first wrote my solution.

Also, thanks for the detailed explanation and sharing your thoughts on when to use uppercase for your consts. Seems like a solid rule I may adopt into my own practice!

Collapse
 
jsjoeio profile image
Joe Previte (he/him)

Really interesting read! When I heard exercism was going to automate solutions, I didn't realize this is what they meant. I thought there was an "easier" way. But it's really fascinating to read about AST parsing.

The way you broke everything down made it feel more approachable than I originally thought. Thanks for sharing this and putting in all the work for the JS/TS communities.

Collapse
 
sleeplessbyte profile image
Derk-Jan Karrenbeld

C# has a different approach for two-fer and Ruby is going to try it out for another solution where they don't do AST interpretation like I do, but do AST matching:

  • parse the AST for a lot of solutions
  • strip the Identifier names and rename them to be consistent e.g. a, b, c or input_arg_1, body_constant_1 (so that two solutions that are identical except for the name use are now actually identical). This is normalisation
  • match the incoming, normalised AST of the solution to the set of known solutions, and have a fixed output.
  • periodically collect everything that is not matched and add these to the analyser.

I think that is an easier approach, but not necessarily a better one. Especially in JavaScript where you have so many ways to write the same thing (this each way creating a new set of permutations).

The way you broke everything down made it feel more approachable than I originally thought.

Feel free to contribute! 💘