diff --git a/.prettierrc b/.prettierrc index 3b59ede..b31f053 100644 --- a/.prettierrc +++ b/.prettierrc @@ -13,6 +13,12 @@ "options": { "printWidth": 53 } + }, + { + "files": ["snippets/snippets/**/ignoreWidth/*.ts"], + "options": { + "printWidth": 120 + } } ] } diff --git a/README.md b/README.md index 9e8428e..6f797df 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,29 @@ # [TypeScript for Beginner Programmers](https://ts.chibicode.com) -### This is the repository for the TypeScript tutorial website called **[TypeScript for Beginner Programmers](https://ycombinator.chibicode.com/)**. +This is the repository for the TypeScript tutorial website called **[TypeScript for Beginner Programmers](https://ycombinator.chibicode.com/)**.
+## Article 3: [Your Coding Tutorial Might Need Some Refactoring](https://ts.chibicode.com/refactor) + + + +## Article 2: [TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App](https://ts.chibicode.com/todo) + + + +## Article 1: [TypeScript Generics for People Who Gave Up on Understanding Generics](https://ts.chibicode.com/generics) + + + ## License & Credits - For emojis, I’m using [Twemoji](https://github.com/twitter/twemoji) by Twitter (CC-BY 4.0 license). @@ -16,6 +34,6 @@ **Shu Uesugi** -- Twitter: [@chibicode](https://twitter.com/chibicode) - [Website](https://chibicode.com) +- Twitter: [@chibicode](https://twitter.com/chibicode) - Email: [shu@chibicode.com](mailto:shu@chibicode.com) \ No newline at end of file diff --git a/public/images/og-refactor.png b/public/images/og-refactor.png new file mode 100644 index 0000000..991e782 Binary files /dev/null and b/public/images/og-refactor.png differ diff --git a/public/images/refactor/asOfWriting.png b/public/images/refactor/asOfWriting.png new file mode 100644 index 0000000..a48e8d0 Binary files /dev/null and b/public/images/refactor/asOfWriting.png differ diff --git a/public/images/refactor/tsdoc.gif b/public/images/refactor/tsdoc.gif new file mode 100644 index 0000000..74f3192 Binary files /dev/null and b/public/images/refactor/tsdoc.gif differ diff --git a/snippets/bin/generateSnippetsBundle.ts b/snippets/bin/generateSnippetsBundle.ts index 149394a..2c7e0ed 100644 --- a/snippets/bin/generateSnippetsBundle.ts +++ b/snippets/bin/generateSnippetsBundle.ts @@ -2,18 +2,29 @@ const glob = require('glob') const fs = require('fs') const chokidar = require('chokidar') +const filePathToKey = (file: string) => + file + .replace(/\.\/snippets\/snippets\/\w+\//, '') + .replace(/longerWidth\//, '') + .replace(/ignoreWidth\//, '') + .replace(/\.ts/, '') + const regenerate = () => { glob('./snippets/snippets/**/*.ts', (_: any, files: readonly string[]) => { + if ( + new Set(files.map(filePathToKey)).size !== files.map(filePathToKey).length + ) { + throw new Error('Duplicate file name') + } + const result = files .map(file => { const contents = fs.readFileSync(file, 'utf8') - return `export const ${file - .replace(/\.\/snippets\/snippets\/\w+\//, '') - .replace(/longerWidth\//, '') - .replace(/\.ts/, '')} = \`${contents + return `export const ${filePathToKey(file)} = \`${contents .trim() .replace(/^;/m, '') - .replace(/`/g, '\\`')}\`` + .replace(/`/g, '\\`') + .replace(/\$/g, '\\$')}\`` }) .join('\n\n') diff --git a/snippets/snippets/refactor/bxzx.ts b/snippets/snippets/refactor/bxzx.ts new file mode 100644 index 0000000..cf0e5ed --- /dev/null +++ b/snippets/snippets/refactor/bxzx.ts @@ -0,0 +1,5 @@ +// 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" diff --git a/snippets/snippets/refactor/crgn.ts b/snippets/snippets/refactor/crgn.ts new file mode 100644 index 0000000..08a0ebe --- /dev/null +++ b/snippets/snippets/refactor/crgn.ts @@ -0,0 +1,6 @@ +// 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? diff --git a/snippets/snippets/refactor/hfdq.ts b/snippets/snippets/refactor/hfdq.ts new file mode 100644 index 0000000..216f386 --- /dev/null +++ b/snippets/snippets/refactor/hfdq.ts @@ -0,0 +1,16 @@ +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%') diff --git a/snippets/snippets/refactor/ignoreWidth/mvsz.ts b/snippets/snippets/refactor/ignoreWidth/mvsz.ts new file mode 100644 index 0000000..a8caf53 --- /dev/null +++ b/snippets/snippets/refactor/ignoreWidth/mvsz.ts @@ -0,0 +1 @@ +function makePair
- Note: If you already understand generics, you
- won’t find anything new in this tutorial. However,{' '}
-
- Note: If you’ve used React, you might have
- realized that
- Note: You need to set{' '}
+ You need to set{' '}
+ 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.
+
+ Similarly,{' '}
+
+ But they can be improved. As someone who’s written many coding
+ tutorials, I realized that{' '}
+
+ However, while many programmers have written guidelines on how to
+ refactor code,{' '}
+
+ 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:
+ Let’s take a look!
+ Even if you’ve never written a coding tutorial,{' '}
+
+
+ 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 “
+
+ Did you notice that the above code is{' '}
+ formatted to fit on a small screen?{' '}
+
+ 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.
+
+ Here’s another example I used for my tutorial called “
+
+ And this is the same code in BAD formatting (
+
+ So, here’s my first refactoring tip:{' '}
+
+ You can ensure this by keeping line length short.{' '}
+ I try to keep it under about Here are some other techniques:
+ Why is this necessary?{' '}
+
+ You might be tempted to assume that your readers will read (and
+ follow along) your coding tutorial on a computer.{' '}
+
+ 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{' '}
+
+ That’s why mobile reading experience is important.{' '}
+
+ The bottom line:{' '}
+
+ Video tutorials: What I’ve said so far applies
+ to text-based tutorials.{' '}
+
+ As of writing, the
+ following code appears on the official{' '}
+
+ TypeScript handbook
+
+ . And the handbook{' '}
+
+ Question:{' '}
+
+ Answer: The official{' '}
+
+ TypeScript handbook
+ {' '}
+ uses the above code to explain{' '}
+
+ I know what you’re thinking: It’s hard to tell! If you look at the
+ code again, only{' '}
+
+ So, in my opinion,{' '}
+
+ If I were to explain how the “
+
+ What I want to say is: Prefer minimal code samples.
+ {' '}
+
+ You might be thinking: “Well, that’s obvious.” But trust
+ me, so many tutorials (including the official TypeScript
+ handbook!) fail at keeping things simple and end up with too much
+ noise in code samples, making them hard to follow.
+
+ Minimal reproducible example: When you ask a
+ question on StackOverflow or file an issue on GitHub, you’re often
+ asked to create a{' '}
+
+ You should use the same principle when writing code samples for
+ your tutorials. Ask yourself:{' '}
+
+ Tutorials vs Documentations: There is a
+ difference in what should code samples be like for{' '}
+ tutorials vs documentations.{' '}
+ Tutorials are for{' '}
+
+ As of writing, the
+ following code appears on the official{' '}
+
+ TypeScript handbook
+ {' '}
+ (right after the example we showed earlier). And the handbook{' '}
+
+ In TypeScript,{' '}
+
+ Now, a question for you:{' '}
+
+ 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:{' '}
+
+ You probably would not. It just does simple
+ string concatenation in reverse.{' '}
+
+ The bottom line:{' '}
+
+ So, here’s my third refactoring tip:{' '}
+ Prefer practical code samples.{' '}
+
+ By showing a practical example,{' '}
+
+ Example: If I were to explain how to use{' '}
+
+
+ This is similar to how UI libraries like{' '}
+
+ styled-system
+ {' '}
+ work. In other words, it’s a practical example.{' '}
+
+ To summarize, always ask yourself:{' '}
+
+
+ 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
+ .{' '}
+
+ For example, on{' '}
+
+ Here’s a simple technique you can use. If you want to teach a new
+ concept (let’s call this “X”),{' '}
+
+ I used this technique on{' '}
+
+ 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 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—
+
+ The bottom line:{' '}
+
+ Fail unexpectedly: It’s also more effective if
+ the failure is
+ Let’s talk about the 3 simple techniques you can
+ use to engage the reader’s brain.
+
+ For example, on{' '}
+
+ First, you implement the
+ To implement this feature, you need to write the{' '}
+
+ After that, you implement the{' '}
+
+ To implement this feature, you need to write the{' '}
+
+ You get the idea.{' '}
+
+
+ By the way, did you notice that I used several analogies in this
+ article?
+
+ Note:{' '}
+
+ So in a sense, TypeScript’s types act as lightweight unit tests
+ that run every time you save (compile) the code. (
+
+
+ In your tutorial,{' '}
+
+ 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.
+ {' '}
+
+ Use graphics that show faces. There’s a reason a
+ lot of ads have someone’s face in it. Our brains are wired to pay
+ attention to faces. Take advantage of this to grab your reader’s
+ attention.
+
+ One of the easiest ways to do this is to use{' '}
+ emojis. Just insert a happy{' '}
+
+ 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 when in doubt, use simpler English words/phrases. It will
+ increase the size of your audience.
+
+ Also: If you’re living in the US,{' '}
+
+ (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,{' '}
+
+ Also,{' '}
+
+ Finally,{' '}
+
+ Finally, when things get hard, be encouraging.{' '}
+ Use phrases like:{' '}
+
+ 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
+ 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”.
+
+ If you have feedback, including other ways to improve coding
+ tutorials, please let me know on{' '}
+
+
- Note: If you already know TypeScript basics,
- you won’t find anything new in this tutorial. However,{' '}
- makeState() is
- similar to the useState(){' '}
- hook.
+ If you’ve used React, you might have realized that{' '}
+ makeState() is similar to the{' '}
+ useState() hook.
"strictPropertyInitialization": false
{' '}
diff --git a/src/pages/refactor.tsx b/src/pages/refactor.tsx
new file mode 100644
index 0000000..69781f0
--- /dev/null
+++ b/src/pages/refactor.tsx
@@ -0,0 +1,1371 @@
+/** @jsx jsx */
+import { css, jsx } from '@emotion/core'
+import PostPage from 'src/components/PostPage'
+import EmojiSeparator from 'src/components/EmojiSeparator'
+import {
+ P,
+ Highlight,
+ Ol,
+ OlLi,
+ Ul,
+ UlLi,
+ ForegroundHighlight,
+ A,
+ Code,
+ Hr,
+ Blockquote
+} from 'src/components/ContentTags'
+import * as snippets from 'src/lib/snippets'
+import TwitterLink from 'src/components/TwitterLink'
+import AboutMe from 'src/components/AboutMe'
+import CodeBlock from 'src/components/CodeBlock'
+import Caption from 'src/components/Caption'
+import CodeResult from 'src/components/CodeResult'
+import CodeResultWrapper from 'src/components/CodeResultWrapper'
+import ResultHighlight from 'src/components/ResultHighlight'
+import Emoji from 'src/components/Emoji'
+import InternalLink from 'src/components/InternalLink'
+import { articlesData } from 'src/lib/articles'
+import { baseUrl } from 'src/lib/meta'
+import { SourceAvailableText } from 'src/components/GitHubButton'
+import TodoWithData from 'src/components/TodoWithData'
+
+const techniques = [
+ {
+ title: (
+ <>
+ Make code samples
+ {techniques.map((technique, index) => (
+
+
+
+
+ printWidth to automate this (my{' '}
+ .prettierrc is{' '}
+
+ here
+
+ ).
+
+
+ letter-spacing to fit more characters.
+
+ &” operator
+ {' '}
+ in TypeScript.
+ &”
+ operator creates an intersection of two types. You can learn
+ more on &” operator
+
+ . There are too many other keywords that are just noise (e.g.{' '}
+ Partial<>, hasOwnProperty,{' '}
+ as, constructor, public,{' '}
+ interface, void, implements
+ , prototype, etc).
+ &”
+
+ , the topic being explained through this code. Every other
+ keyword is just noise!
+ >
+ }
+ />
+ &” operator
+ {' '}
+ in TypeScript. To make matters worse,{' '}
+ as of writing, this
+ is the ONLY code sample used to explain the “&”
+ operator on the official handbook!
+ &” operator works
+ in TypeScript,{' '}
+ &”
+ operator.
+ &” operator
+ works, I’d rewrite the above code as follows—much simpler!
+ >
+ }
+ />
+
+ |”
+ {' '}
+ symbol
+ number | string
+ {' '}
+ to specify that a parameter can either be number OR{' '}
+ string
+ padding parameter can
+ be either number or string.
+
+
+ padding is number, then that number
+ of spaces is added to the left side of value.
+ padding is string, then{' '}
+ padding is added to the left side of{' '}
+ value.
+ padLeft() a good example to explain how{' '}
+ the{' '}
+
+ “|”
+ {' '}
+ operator works in TypeScript?
+ |”
+ {' '}
+ operator works?
+ >
+ }
+ />
+ padLeft() for the case where the
+ second parameter is string?
+ 'Jim: ' + 'Hello World'.
+ padLeft() should support
+ the second string parameter.
+ padLeft(value, padding) is useful if{' '}
+ padding is number but is{' '}
+ padding is string.
+ padding’s type as{' '}
+ number | string is not useful—it could just be{' '}
+ number. That’s why this is NOT a good example.
+
+ 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:
+ paddingLeftCss() can take a number or{' '}
+ string:
+
+
+ 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.
+ string, it just uses that string as{' '}
+ padding-left.
+ number or string,
+
+ 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.
+
+
+
+
+ toggleTodo() function, and in that process, I explain
+ TypeScript types, read-only properties, and mapped types.
+ completeAll() function, and in that process, I
+ explain{' '}
+ array types, literal types, and intersection types in
+ TypeScript.
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+