[NextJS] Blog Reorganization Journal - 2. Clothing It in TypeScript
[NextJS] Blog Reorganization Journal - 2. Clothing It in TypeScript
When I first encountered JavaScript and grew somewhat comfortable with its basic concepts, I heard a story that was passed around like a legend in the developer community.
-----
"In the beginning, developers used a scripting language to perform various actions on the web, and it is said that this came to be known today as JavaScript."
"For decades under JavaScript's rule, developers were forced to pass through JavaScript to use any script at all."
"JavaScript was powerful, but of an indolent nature — it did not check the arguments it received, and unjustly placed the entire burden of responsibility for those arguments upon the developer alone."
"One day, a mysterious object came in place of the string it expected to receive as an argument, and because it failed to check this properly, it wrought great calamity upon someone's site — and the person in charge of that site was said to have suffered, unable to go home, for two days."
"One day, among the scripts, one who possessed Type appeared."
"This one was unique in that it could verify its arguments directly, blocking any wrongly given argument and admitting only the correct ones. By its power, it is said that IDEs and developers wielding TypeScript could easily infer its types and thereby avert calamity."
"On the day this one is made manifest, a new revolution shall come upon ECMA, and developers shall be saved."
-----
I was captivated by this legend-like tale, but never imagined that TypeScript would actually descend into my own code.
Then one day — the very day I resolved to overhaul my blog — it arrived at my code and graciously revealed its power to me.
Now, I believed, I could be delivered from the vagueness and sloppiness of JavaScript.
But what I believed turned out to be wildly, laughably wrong.
The method of summoning "Lord" TypeScript into NextJS turned out to be surprisingly simple.
BASH
# NPM-based npx create-next-app --ts # Yarn-based yarn create-next-app --typescript
Entering the above command generates a template based on "Lord" TypeScript.
BASH
# NPM-based npm install typescript @types/react @types/node --save-dev # Yarn-based yarn add typescript @types/react @types/node --dev
Enter the above command to install the plugins related to "Lord" TypeScript.
Then simply create a tsconfig.json file at the root path. Here you can "petition" in detail exactly how "Lord" TypeScript should exercise its power.
JSON
{ "compilerOptions": { "target": "ESNext", "lib": [ "dom", "dom.iterable", "esnext", "ES2021" ], "allowJs": true, "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "baseUrl": ".", "paths": { "@commons/*": [ "commons/*" ], "@components/*": [ "components/*" ], "@pages/*": [ "pages/*" ], "@styles/*": [ "styles/*" ] } }, "include": [ "next-env.d.ts", "**/*.ts", "**/*.tsx" ], "exclude": [ "node_modules" ] }
The petition I offered up is above — take it as reference. The Scripture of TypeScript contains fine examples of such petitions.
If you started with "New project," the Lord's Prayer has already been inscribed for you, so refer to that instead.
Your existing React components are all probably branded with the unclean extensions .js and .jsx. Strip away this stain entirely and replace it with .ts and .tsx. How sacred this is! Proudly inscribe this mark upon every component you create from here on.
The only thing that changes from JavaScript is the presence of types. Since the syntax and usage remain the same, "Lord" TypeScript pays no mind at all even if you treat it exactly as before.
With but a single gesture, all things were given type.
"Lord" TypeScript, quite literally, makes type inference possible. By assigning a type to every argument and return value, it lets the developer know exactly what type that argument and return value carry.
JAVASCRIPT
/** * 숫자 두 개 뺏기고 문자열 하나 받기 * * @param {number} n: 소중한 숫자 * @param {number} m: 귀중한 숫자 * * @returns {string} 근본없는 문자열 */ function add(n, m) { const temp = n + m; return `${n} + ${m} = ${temp}`; }
TYPESCRIPT
/** * 덧셈의 권능 목도하기 * * @param {number} n: 미천한 숫자 * @param {number} m: 가련한 숫자 * * @returns {string} 광휘의 문자열 */ function add(n: number, m: number): string { const temp: number = n + m; return `${n} + ${m} = ${temp}`; }
Behold this difference. As you can see in the example above, the gap between JavaScript and "Lord" TypeScript is as vast as the wilderness. It bestows a type upon every single argument and variable.
Thus, it prevents heretics from entering or exiting through a function's arguments or return value.
You might ask whether this is simply doing unnecessary work that wasn't needed before? There was a heretic who said the very same thing yesterday, and is no longer with us.....
As his disciple struck the keyboard upon the code just as he had commanded, the wilderness that was the development timeline split in two.
If you've used VSCode, you'll know that beyond JavaScript's basic API, autocomplete doesn't work well for APIs the developer designed themselves.
This is a natural consequence — the IDE has no way of knowing the exact structure of a script the developer wrote themselves, since it isn't in the dark. Since it isn't declared in a fixed structure like JAVA or C-family languages, the IDE has no way of knowing what's what.
But with "Lord" TypeScript, it's different. Since every type and structure of the code you write is declared in advance, the IDE understands this and shows the sub-objects of that element through autocomplete. This kind of feature leads to high development productivity. In other words, it personally performs the miracle of the loaves and fishes, producing more output from the same resources.
Come to me, all who are weary and burdened, and I will give you rest.
For the reasons above, JavaScript was a language where applying object-oriented patterns was awkward (not impossible, though).
But by bestowing type upon everyone, "Lord" TypeScript made it useful for applying object-oriented patterns.
Thanks to this, concepts like classes and interfaces can be applied easily, so that those coming from object-oriented languages like JAVA can settle in more comfortably.
To willingly take on such a troublesome entity and bring unnecessary hardship upon oneself is, truly, a lamentable thing.
Even in areas where you wouldn't need to think twice in JavaScript, TypeScript forces certain areas upon you because of its precious "types."
For example, in order for a child component to receive props, you have to declare the type in advance via an interface.
TYPESCRIPT
interface Props { width: number, height: number, color?: string } /** * 컴포넌트 ReactElement 하사 받기 * * @param {Props} param0: 프로퍼티 * * @returns {ReactElement} ReactElement */ export default function Component({ width, height, color }: Props): ReactElement { return <div style={{ width: width, height: height, color: color || 'white' }}>테스트</div> }
Just to declare a single type, you have to design an entire interface like this — it requires more work to accomplish the same thing.
This kind of formality piles superficial trappings onto the code, resulting in a brutal blow to code readability.
And despite its grandiose name, underneath it all, it's ultimately no different from JavaScript. However much it puts on airs of being something great and important, the entity actually carrying out the work is still the same JavaScript. Which means it shares JavaScript's fundamental flaws and weaknesses just the same. If this isn't hypocrisy, what is?
The inconvenience arising from types was understandable enough — types do come with clear benefits, after all.
Even so, what I truly could not come to terms with about TypeScript lies in its merciless rules.
TYPESCRIPT
/** * 테스트 "목도"하기 * * @param {*} color: 색상 * * @return {*} 요소 */ function test(color) { const ref = useRef(); useEffect(() => { ref.current.style.backgroundColor = color; }); return <div ref={ref}>Color</div>; }
How does the code above look to you? It looks perfectly ordinary at a glance. And not just in appearance — it actually runs fine as JavaScript with no issues. Yet TypeScript, by its strict rules, refuses to allow this.
The code above contains the following "grave" errors.
- The type of color is not specified
- The return value of the test function is not specified
- The target of useRef() is not specified
- ref and ref.current might not exist
For these reasons, it gets rejected right from the compilation stage.
Number 4 is especially absurd — even though the element clearly exists, and isn't even dynamically rendered but has been written into the HTML from the very beginning, TypeScript still balks with a "Hm? That might not exist, you know? Are you really, truly sure??" and refuses to proceed. Even a devout follower would go mad and spin around a full 2,160 degrees. I'm fairly confident that if you push them just a little further past that point, they'd actually levitate for a few seconds.
TYPESCRIPT
/** * 테스트 "목도"하기 * * @param {number} color: 색상 * * @return {ReactElement} 요소 */ function test(color: string): ReactElement { const ref = useRef<HTMLDivElement>(null); useEffect((): void => { if (ref && ref.current) { ref.current.style.backgroundColor = color; } }); return <div ref={ref}>Color</div>; }
The correct usage is above. How utterly pointless. Fine, precise verification, sure — but this borders on paranoia, assuming by default that every single element "doesn't exist." I already know something exists, yet I have to personally re-prove the existence of something whose existence has already been proven, through all manner of pointless formality.
Even so, the power of "Lord" TypeScript was more than enough to point JavaScript development in a new direction.
Especially for someone like me, who started out with tightly object-oriented languages like JAVA or C# first, I really disliked JavaScript's vagueness — and TypeScript, through types and structure, was more than capable of resolving that discomfort.
That said, if you're not yet comfortable with JavaScript or React, embracing "Lord" TypeScript might still be premature. As recorded in the heretic's account above, the power of "Lord" TypeScript demands surrounding code that didn't exist before, which will naturally raise the barrier to entry for the code.
Too much of a good thing is still too much. Even present-day religions inevitably run into trouble if they blindly worship something while neglecting their actual work. Don't let yourself get so caught up trying to blindly receive the power of "Lord" TypeScript that you end up botching what actually matters.
What on earth did I just write...
