TypeScript for Beginner Programmers

· By Shu Uesugi (@chibicode)

Your Coding Tutorial Might Need Some Refactoring

Slide 1 / 8

Both code and coding tutorials can be refactored

At some point in your coding career, you’ve probably come across a piece of spaghetti code that’s not so reader-friendly. It needed some refactoring.

Spaghetti code needs refactoring

Similarly, you’ve probably also come across a coding tutorial that’s not so reader-friendly. Maybe you wanted to learn a new language, library, or framework, but the tutorial you found made you more frustrated than before.

But they can be improved. As someone who’s written many coding tutorials, I realized that most coding tutorials can be refactored to be more reader-friendly—just like refactoring code.

Coding tutorials can also be refactored

However, while many programmers have written guidelines on how to refactor code, guidelines on how to refactor coding tutorials are rare.

So, in this article, I’ll share six opinionated tips on refactoring coding tutorials. I’ve used these techniques on my own tutorials to make them more reader-friendly. Here’s the list:

  1. Make code samples mobile-ready
  2. Prefer minimal code samples
  3. Prefer practical code samples
  4. Fail fast
  5. Use themes, analogies, and quizzes
  6. Add a thoughtful touch

Let’s take a look!

Side Note

Even if you’ve never written a coding tutorial, you might know someone who has (maybe one of your Twitter followers). I’d appreciate it if you could share this article with them. You can click here to tweet this article.

The source code for this site is on GitHub:

Slide 2 / 8

Refactoring Tip 1 of 6:

Make code samples mobile-ready

Take a look at the code below. It’s in TypeScript, but don’t worry if you don’t know TypeScript. I used it for my tutorial called “TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App”:

type Todo = Readonly<{
id: number
text: string
done: boolean
place: Place
}>

Did you notice that the above code is formatted to fit on a small screen? Because each line length is short (max 31 chars), you can read it without side-scrolling on most phones.

If the above code was formatted in a single line like below instead, you’d have to side-scroll or wrap text on a small screen, which hurts readability.

Max line length: 79 characters
Line is too long; must scroll or wrap on a phone
type Todo = Readonly<{ id: number; text: string; done: boolean; place: Place }>

Here’s another example I used for my tutorial called “TypeScript Generics for People Who Gave Up on Understanding Generics”. This is good formatting ( fits on a small screen):

Max line length: 28 characters
function makePair<
F extends number | string,
S extends boolean | F
>() {}

And this is the same code in BAD formatting ( doesn’t fit on a small screen):

Max line length: 72 characters
Line is too long; must scroll or wrap on a phone
function makePair<F extends number | string, S extends boolean | F>() {}

So, here’s my first refactoring tip: Make code samples in your tutorials mobile-ready.

You can ensure this by keeping line length short. I try to keep it under about 50 characters (at 14px font size). I use Prettier with custom printWidth to automate this (my .prettierrc is here).

I use Prettier to keep line length short

Here are some other techniques:

  • Prefer shorter variable names (but don’t sacrifice code readability).
  • If you can customize the CSS, use narrow coding fonts. I use Iosevka—it’s slim and looks great. You can also tighten letter-spacing to fit more characters.
  • If you want code samples to have longer line length on a larger screen, you can use Prettier in the browser to dynamically adjust line length based on window width.

Why is this necessary? Because many people actually read coding tutorials on their phones.

You might be tempted to assume that your readers will read (and follow along) your coding tutorial on a computer. But that’s a bad assumption.

In the past, I’ve used Google Analytics to track desktop vs mobile usage on my coding tutorials. Even though my tutorials are meant to be done on a computer, surprisingly many people accessed them from a mobile device.

This is because many people discover coding tutorials while using a phone to browse Twitter, mailing lists, and online forums.

Many people discover coding tutorials while using a phone to browse Twitter, etc.

That’s why mobile reading experience is important. If you can easily read all the code samples on a phone, you might be able to finish the tutorial without pulling out your computer. Sometimes you need to follow along on your computer to fully understand the content, but that’s not always the case.

The bottom line: Assume that people will discover your coding tutorial on their phone and try to deliver the best possible first impression.

Side Note

Video tutorials: What I’ve said so far applies to text-based tutorials. For video tutorials (screencasts), it’d be ideal if the fonts are large enough to be legible on a phone (in landscape mode). I like watching coding tutorials on YouTube, but sometimes the fonts are too small when viewed on my phone.

Slide 3 / 8

Refactoring Tip 2 of 6:

Prefer minimal code samples

As of writing, the following code appears on the official TypeScript handbook (which is written in a way that’s both a tutorial and documentation). And the handbook uses this code to explain how to use a particular TypeScript keyword/operator.

Question: Can you tell which keyword/operator is being explained through this code? Hint: It is one of the keywords/operators used in the code. (You don’t need to know TypeScript—just guess!)

function extend<First, Second>(
first: First,
second: Second
): First & Second {
const result: Partial<First & Second> = {}
for (const prop in first) {
if (first.hasOwnProperty(prop)) {
;(result as First)[prop] = first[prop]
}
}
for (const prop in second) {
if (second.hasOwnProperty(prop)) {
;(result as Second)[prop] = second[prop]
}
}
return result as First & Second
}
class Person {
constructor(public name: string) {}
}
interface Loggable {
log(name: string): void
}
class ConsoleLogger implements Loggable {
log(name) {
console.log(`Hello, I'm ${name}.`)
}
}
const jim = extend(
new Person('Jim'),
ConsoleLogger.prototype
)
jim.log(jim.name)

Answer: The official TypeScript handbook uses the above code to explain how to use the &” operator in TypeScript.

(Brief explanation: In TypeScript, the “&” operator creates an intersection of two types. You can learn more on my tutorial.)

But it’s hard to tell! If you look at the code again, only the highlighted part below is related to the “&” operator. There are too many other keywords that are just noise (e.g. Partial<>, hasOwnProperty, as, constructor, public, interface, void, implements, prototype, etc). You also need to pause and read carefully to understand what’s going on, even though most of the code isn’t directly related to the “&” operator.

Only the highlighted part is related to &, the topic being explained through this code. Every other keyword is just noise.
function extend<First, Second>(
first: First,
second: Second
): First & Second {
const result: Partial<First & Second> = {}
for (const prop in first) {
if (first.hasOwnProperty(prop)) {
;(result as First)[prop] = first[prop]
}
}
for (const prop in second) {
if (second.hasOwnProperty(prop)) {
;(result as Second)[prop] = second[prop]
}
}
return result as First & Second
}
class Person {
constructor(public name: string) {}
}
interface Loggable {
log(name: string): void
}
class ConsoleLogger implements Loggable {
log(name) {
console.log(`Hello, I'm ${name}.`)
}
}
const jim = extend(
new Person('Jim'),
ConsoleLogger.prototype
)
jim.log(jim.name)

So, in my opinion, the above code sample is NOT a great way to explain how to use the “&” operator in TypeScript. It does show a few places where the “&” operator can be used, which is good, but it could have been done without adding so much noise.

If I were to explain how the “&” operator works in TypeScript, I’d refactor the earlier code as follows—it basically does the same thing in a simpler way. You don’t need to understand TypeScript to know that this is more minimal and focused on explaining how to use the “&” operator. The amount it takes to understand this code is also much shorter.

If I were to explain how the “&” operator works, I’d rewrite the above code as follows—much simpler!
type Person = { name: string }
type Loggable = { log: (name: string) => void }
// Use & to make jim BOTH Person AND Loggable
const jim: Person & Loggable = {
name: 'Jim',
log: name => {
console.log(`Hello, I'm ${name}.`)
}
}
// "Hello, I’m Jim."
jim.log(jim.name)

If you want to talk about more advanced usage, you could add the advanced example AFTER my simple example.

What I want to say is: Prefer minimal code samples, at least initially. If you’re trying to teach a new concept (let’s call this “X”), just focus on X in the code sample and don’t add too much extra stuff. Add extra stuff only (1) after you showed a simple example and (2) when it really helps the reader’s understanding.


Minimal reproducible example: When you ask a question on StackOverflow or file an issue on GitHub, you’re often asked to create a minimal reproducible example. Your code needs to be as small as possible, such that it is just sufficient to demonstrate the problem, but without any additional complexity (Wikipedia’s definition).

You should use the same principle when writing code samples for your tutorials. Ask yourself: Can I make this code sample more minimal while maintaining the learning experience?

Side Note

Tutorials vs Documentations: There is a difference in what should code samples be like for tutorials vs documentations. Tutorials are for learning, so it’s often better to keep code samples minimal to reduce confusion (except for advanced topics). Documentations are references, so it’s often better to have comprehensive code samples.

The official TypeScript handbook I mentioned earlier is written as something that's in between a tutorial and documentation. As of writing, I believe it’s subpar as a tutorial (because of what I said above) and as documentation (it isn’t as focused and detailed as it should be).

To learn more about different kinds of documentation, take a look at this excellent post by Daniele Procida: “What nobody tells you about documentation”.

Slide 4 / 8

Refactoring Tip 3 of 6:

Prefer practical code samples

As of writing, the following code appears on the official TypeScript handbook (right after the example we showed earlier). And the handbook uses this code to explain how to use the union operator in TypeScript, which is the | symbol highlighted below.

(Slightly modified from the original for readability)
/**
* Takes a string and adds "padding" to the left.
*
* If 'padding' is a number, then that number of
* spaces is added to the left side.
*
* If 'padding' is a string, then 'padding' is
* appended to the left side.
*/
function padLeft(
value: string,
padding: number | string
) {
if (typeof padding === 'number') {
return Array(padding + 1).join(' ') + value
} else {
return padding + value
}
}

In TypeScript, you can write number | string to specify that a parameter can either be number OR string. So in this case, the second padding parameter can be either number or string.

  • If padding is number, then that number of spaces is added to the left side of value.
  • If padding is string, then padding is added to the left side of value.
// If the second parameter is number, then that
// number of spaces is added to the left side
padLeft('Hello world', 4)
// → " Hello world"
// If the second parameter is string, then
// that string is appended to the left side
padLeft('Hello world', 'Jim: ')
// → "Jim: Hello world"

Now, a question for you: Is padLeft() a good example to explain how the | operator works in TypeScript?

Is the above code a good example to explain how the | operator works?

I’d say NO—it’s NOT a good example. You don’t need to know TypeScript to see why.

Take a look below and ask yourself: Would you EVER use padLeft() for the case where the second parameter is string?

// If the second parameter is string, then
// that string is appended to the left side
padLeft('Hello world', 'Jim: ')
// → "Jim: Hello world"
// Ask yourself: Would you EVER do this?

You probably would not. It just does simple string concatenation in reverse. You probably would just use other standard ways to concatenate strings, such as 'Jim: ' + 'Hello World'. There’s no good reason why padLeft() should support the second string parameter.

The bottom line: padLeft(value, padding) is useful if padding is number but is pretty useless if padding is string. So setting padding’s type as number | string is not useful—it could just be number. That’s why this is NOT a good example.


So, here’s my third refactoring tip: Prefer practical code samples. Avoid showing code no one would write. If you’re trying to teach a new concept (let’s call this “X”), come up with a practical code sample where X is actually useful in solving the problem.

By showing a practical example, readers will understand why X is worth learning. If you show them a useless example, they’d think: “What’s the point of learning X?”

Example: If I were to explain how to use number | string in TypeScript, instead of the earlier padLeft() example, I would use the following paddingLeftCss() function. The name is similar, but this one is used to generate a CSS padding-left string:

function paddingLeftCss(val: number | string) {
if (typeof val === 'number') {
return `padding-left: ${val * 0.25}rem;`
} else {
return `padding-left: ${val};`
}
}
// padding-left: 0.25rem;
paddingLeftCss(1)
// padding-left: 0.5rem;
paddingLeftCss(2)
// padding-left: 10%;
paddingLeftCss('10%')

paddingLeftCss() can take a number or string:

  • If it’s number, it returns padding-left CSS that’s a multiple of a predefined spacing unit. In this case, 1 = 0.25rem, 2 = 0.5rem, etc. This would be helpful for visual consistency when designing UI.
  • If it’s string, it just uses that string as padding-left.

This is similar to how UI libraries like styled-system work. In other words, it’s a practical example. It actually makes sense to have the parameter be either number or string, unlike the previous example.


To summarize, always ask yourself: Is my code sample practical? Would anyone ever write code like this?

Side Note

padLeft() could have been useful if you could pass both a number AND a string, and have it repeat the string the specified number of times (see below). But that’s not what was in the handbook.

// It could have been useful if you could pass
// both number AND string, and have it repeat
// the string the specified number of times
padLeft('Hello world', 4, '#')
// → "####Hello world"
Slide 5 / 8

Refactoring Tip 4 of 6:

Fail fast

One of the best ways to capture your reader’s attention is to FAIL. When things don’t go according to plan, people will pay more attention than when everything goes smoothly. Use this to your advantage.

Furthermore, it’s more effective if you fail fast. Try to show a failing scenario as early as possible in your article. By doing so, you’ll be able to capture your reader’s attention right off the bat.

For example, on my TypeScript tutorial, I start with an example where, if you run the code, the actual result is different from the expected result (failure). Then I talk about how to prevent failures like this using TypeScript’s features.

The first example on my tutorial: The actual result is different from the expected result
Expected:
{ id: 1, text: '…', done: false }
Actual:
{ text: '…', done: false }

Here’s a simple technique you can use. If you want to teach a new concept (let’s call this “X”), start with a concrete scenario where things fail or aren’t ideal when you don’t use X. Then, use X to solve the problem. Your readers will pay more attention and also understand why X is worth learning.

I used this technique on my TypeScript generics tutorial. Early in the article, I attempt to solve a problem that can only be solved by generics…without using generics. Of course, I fail. Then, I use generics to successfully solve the problem.


In a way, this is similar to test driven development (TDD). In TDD, you write a failing test first, and after you watch it fail, you try to make it pass. Similarly, in a coding tutorial, it’s more effective if you show a failing example first and have the readers watch it fail.

In TDD, you write a failing test first. In a coding tutorial, make readers go through a failing example first

It’s tempting to be lazy and skip writing a failing test in TDD. Similarly, when writing a coding tutorial, it’s tempting to skip showing a failing example and just start with a successful example. But resist this temptation—failure is your friend in expository writing.

The bottom line: Double-check to see where the first failing example appears in your tutorial. If it’s missing, add one near the beginning.

Side Note

Fail unexpectedly: It’s also more effective if the failure is surprising. Trick your reader into thinking that a code sample would work perfectly…then make it fail. Make your readers think, “WTF? How come it doesn’t work?”—and they’ll be more curious. Unexpected failure = more memorable learning experience.

Slide 6 / 8

Refactoring Tip 5 of 6:

Use themes, analogies, and quizzes

Let’s talk about the 3 simple techniques you can use to engage the reader’s brain.


First, use themes:If your tutorial doesn’t have an underlying theme that ties together your examples, try to add one. Having an extremely simple theme is better than having no theme.

For example, on one of my tutorials, I teach 8 beginner TypeScript topics (types, read-only properties, mapped types, array types, literal types, intersection types, union types, and optional properties). Instead of covering each topic separately, I use a simple theme of building a todo app to explain all those 8 topics. The idea is to add features to a todo app one by one using TypeScript.

First, you implement the “toggle todo” feature of a todo app. This lets you check and uncheck the checkboxes—try it below!

First todo
Second todo
↑ Check and uncheck the checkboxes!

To implement this feature, you need to write the toggleTodo() function, and in that process, I explainTypeScript types, read-only properties, and mapped types.

After that, you implement the “mark all as completed” feature, which checks all the checkboxes at once.

First todo
Second todo
Mark all as completed
↑ Try pressing “Mark all as completed”

To implement this feature, you need to write the completeAll() function, and in that process, I explain array types, literal types, and intersection types in TypeScript.

You get the idea. When I want to teach many concepts at once, I prefer to use a specific theme to explain them all—in this case, building a todo app. By doing so, readers won’t have to do as much context switching in their head.


Second, use analogies to explain new concepts. Tie a new concept with the concept your reader already knows.

By the way, did you notice that I used several analogies in this article?

  • I compared refactoring code with refactoring a coding tutorial.
  • I compared minimum reproducible examples with minimal code samples.
  • I compared TDD with using failing examples.

Note: If your analogy isn’t perfect, use it anyway but say “it’s not a perfect comparison.” It would still help your reader memorize the concept. I did this on my TypeScript tutorial when I compare TypeScript’s type-checking feature with unit tests. Here’s what I wrote:

So in a sense, TypeScript’s types act as lightweight unit tests that run every time you save (compile) the code. (Of course, this analogy is a simplification. You should still write tests in TypeScript!)


Finally, use quizzes to make your readers pause, think, and be engaged.

In your tutorial, count how many times you ask simple questions, such as “what would happen if you do X?” or “what’s wrong with the following code?”. Even non-interactive, simple yes-no quizzes are better than having no quiz!

Slide 7 / 8

Refactoring Tip 6 of 6:

Add a thoughtful touch

This is the final section! Here are some mini-tips to add a thoughtful touch to your tutorials.


Visually emphasize important parts in your code samples. Highlight or bold important words/lines so your readers know what to pay attention to. You can also add a comment next to the emphasized words/lines for more clarity.

Highlight or bold important words/lines
function toggleTodo(todo) {
return {
// This line was missing
id: todo.id,
text: todo.text,
done: !todo.done
}
}

Use mostly-text graphics. Graphics made of basic shapes and texts are simple yet effective. For example, I used this graphic earlier:

It takes a only few minutes to create, but it helps your readers visually remember the idea.


Avoid difficult English words/phrases. I spent a year traveling the world in 2018, and one thing I learned is that so many people in the world can speak some English, but many don’t speak English well. Globally, there are 3x as many non-native English speakers as native English speakers.

So when in doubt, use simpler English words/phrases. It will increase the size of your audience.

Use simple English

Also: If you’re living in the US, avoid cultural references and humor that only people familiar with American culture would understand. Always ask yourself: “Would someone living far, far away from where I live understand what I’m writing?”

(Note: I’m an English-Japanese translator, and I often find it really hard to translate some cultural references into Japanese. If you write a good technical article, people will volunteer to translate it. Try to make it easy for the translators by minimizing the number of cultural references you use!)


When you skip a step or assume prerequisite knowledge, say so. Sometimes you have to skip some steps to keep your tutorial concise. But skipping steps can also lead to confusion.

So if possible, let your reader know what steps you’re skipping—by doing so, they won’t be as confused, and they can Google how to do the missing steps if necessary.

Be explicit: “Skipping !”

Also, if your tutorial requires some prerequisite knowledge,explicitly list them. For example, my TypeScript generics tutorial assumes prior knowledge of closure and ES2015 syntax. I mention this and added MDN documentation links in case the reader is unfamiliar with them.

Finally, check if you’re using a convention that newcomers may not know about. On my TypeScript generics tutorial, I explain why generic type parameters are often written using a single uppercase letter (like T, E, K, V, etc). This convention is actually borrowed from Java—it’s mentioned in the official Java documentation. Beginner programmers who have never touched Java may not know about this, so I explained it on my tutorial.


Finally, when things get hard, be encouraging. Use phrases like: “This topic is harder than other topics we’ve covered. Don’t worry if you don’t get immediately—just keep reading and you’ll get it eventually!” A little touch of empathy can go a long way.

Slide 8 / 8

Parting thoughts

In 2017, Dan Abramov from the React.js team gave an excellent talk called “The Melting Pot of JavaScript”. He talked about how one should approach building tools (like React, create-react-app, etc) that many beginners/newcomers use. Here’s his quote:

If you’re building tools like me, there’s this fact that we have become the new gatekeepers to one of the largest programming communities in the world.

And this is scary stuff. Because it means that every time our tool prints an incomprehensible error message, somebody somewhere decides that they’re just not cut out for programming. And this is a big responsibility.

[...] If you’re a maintainer of an open-source project it is invaluable to go out there in the field and see what they struggle with as they try to use your projects.

And if you think improving newcomer experience is polish, it’s not polish. If you go out there in the field you will see that it makes a real difference in people’s lives, and what they can learn, and what they can build with it. So it’s not just polish. Take this seriously.

I think his quote applies not just to coding tools, but also to coding tutorials.

You don’t have to follow all the guidelines I mentioned on this page. Sometimes you have to break the rule when refactoring code—and the same is true for refactoring tutorials. But do try to revise as much as possible. As the saying goes, “Writing is rewriting”.


You can click here to tweet this article. Also, I’d love it if you could like or retweet the tweet below! ↓

Side Note

About the author: I’m Shu Uesugi, a software engineer. The most recent TypeScript project I worked on is an interactive computer science course called “Y Combinator for Non-programmers”.

You can learn more about me on my personal website. My email is shu@chibicode.com.

More Articles

Source available on GitHub: