2024-06-17.md
π‘DIL: μ΄νν°λΈ νμ μ€ν¬λ¦½νΈ
μ€ν°λ: μκ° CS, https://github.com/monthly-cs/2024-05-effective-typescript
μμ±μΌ: 2024-06-17
μμ±μ: dusunax
μ½λλ₯Ό μμ±νκ³ μ€ννκΈ°
μμ΄ν 53: νμ μ€ν¬λ¦½νΈ κΈ°λ₯보λ€λ ECMAScript κΈ°λ₯μ μ¬μ©νκΈ° Prefer ECMAScript Features to TypeScript Features
μλ μμ μ
λ°μΌνλ‘ νμ μ€ν¬λ¦½νΈ μ΄κΈ° 2010λ κ²½, μλ°μ€ν¬λ¦½νΈλ ν΄λμ€, λ°μ½λ μ΄ν°, λͺ¨λ μμ€ν κ°μ κΈ°λ₯μ΄ μμλ€...
μλ°μ€ν¬λ¦½νΈλ νλ μμν¬λ νΈλμ€νμΌλ¬λ‘ μ΄λ₯Ό 보μνμκ³ , νμ μ€ν¬λ¦½νΈλ μ΄κΈ° λ²μ μλ λ 립μ μΌλ‘ κ°λ°ν ν΄λμ€, enum, λͺ¨λ μμ€ν μ ν¬ν¨μν¬ μ λ°μ μμλ€...
μκ°μ΄ νλ₯΄λ©°... μλ°μ€ν¬λ¦½νΈ νμ€ κΈ°κ΅¬μΈ TC39λ λΆμ‘±νλ μ λ€μ λ΄μ₯ κΈ°λ₯μΌλ‘ μΆκ°νλλ° μ΄λ νμ μ€ν¬λ¦½νΈ μ΄κΈ° λ²μ μμ λ 립μ μΌλ‘ κ°λ°νλ κΈ°λ₯κ³Ό, μλ°μ€ν¬λ¦½νΈμ μλ‘ μΆκ°λ κΈ°λ₯μ΄ νΈνμ± λ¬Έμ λ₯Ό λ°μμν€λ μμΈμ΄ λλ€...
νμ μ€ν¬λ¦½νΈ μ§μμ (1) νμ μ€ν¬λ¦½νΈ μ΄κΈ° λ²μ ννλ₯Ό μ μ§νκΈ° μν΄ μλ°μ€ν¬λ¦½νΈ μ κ· κΈ°λ₯μ λ³ννμ¬ λΌμ λ§μΆκ±°λ, (2) μλ°μ€ν¬λ¦½νΈμ μ κ· κΈ°λ₯μ κ·Έλλ‘ μ±ννκ³ νμ μ€ν¬λ¦½νΈ μ΄κΈ° λ²μ μ ν¬κΈ°νλ λ κ°μ§ μ λ΅ μ€ νλλ₯Ό μ ννκ² λλ€...
λλΆλΆμ μ§μμ΄ λ λ²μ§Έ μ λ΅μ μ ννμκ³ , κ·Έ μ΄νλ‘ TC39λ λ°νμ κΈ°λ₯μ λ°μ μν€κ³ , νμ μ€ν¬λ¦½νΈ νμ νμ κΈ°λ₯λ§ λ°μ μν¨λ€λ λͺ νν μμΉμ μΈμ°κ³ μ΄λ₯Ό μ§μΌμ€κ³ μλ€.
κ·Έλ°λ° μ΄ μμΉμ΄ μΈμμ§κΈ° μ μ΄λ―Έ μ¬μ©λκ³ μλ λͺ κ°μ§ κΈ°λ₯μ΄ μμλ€. μ΄ κΈ°λ₯λ€μ νμ 곡κ°κ³Ό κ° κ³΅κ°μ κ²½κ³λ₯Ό νΌλμ€λ½κ² λ§λ€κ² λλ€... λ€μ νμ μ΄μ΄μ...
μ΄κ±°ν enum
- κ°μ λͺ¨μ! μ΄κ±°ν!
- κ°μ λͺ¨μμ λνλ΄κΈ° μν΄μ λ§μ μΈμ΄μμ μ¬μ©ν¨
enum Flavor {
Vanilla = 0,
Chocolate = 1,
Strawberry = 2,
}
let flavor = Flavor.Chocolate;
// ^? let flavor: Flavor
Flavor; // Autocomplete shows: Vanilla, Chocolate, Strawberry
Flavor[0]; // Value is "Vanilla"
νμ μ€ν¬λ¦½νΈ μ΄κ±°ν enumμ λ¬Έμ μ
- μ«μν μ΄κ±°νμμλ μ μλ μ«μ μΈμ κ°μ΄ ν λΉλ μ μλ κ°λ₯μ± => μλνμ§ μμ λμ μ΄λ
let flavor: Flavor = 3; // μ€λ₯κ° λ°μνμ§ μμ, μλνμ§ μμ κ° ν λΉ
- μμ μ΄κ±°ν(const enum)μ λ°νμμ μμ ν μ κ±°λμ΄ μ΅μ νλ¨(μ΄κ±°ν κ°μ΄ μ¬λΌμ§)
const enum Flavor {
Vanilla = 0,
Chocolate = 1,
Strawberry = 2,
}
let flavor = Flavor.Chocolate;
-
js μ»΄νμΌ
-
tsconfig.jsonμ preserveConstEnums νλκ·Έ μ¬μ©
{
"compilerOptions": {
"preserveConstEnums": true,
// ...
}
}
// js
var Flavor;
(function (Flavor) {
Flavor[Flavor["Vanilla"] = 0] = "Vanilla";
Flavor[Flavor["Chocolate"] = 1] = "Chocolate";
Flavor[Flavor["Strawberry"] = 2] = "Strawberry";
})(Flavor || (Flavor = {}));
var flavor = Flavor.Chocolate;
λ¬Έμμ΄ μ΄κ±°ν
- λ¬Έμμ΄ μ΄κ±°νμ λ°νμμλ μμ μ± & ν¬λͺ
μ± μ 곡
- νμ
μ€ν¬λ¦½νΈμ λ€λ₯Έ νμ
κ³Ό λ¬λ¦¬ ꡬ쑰μ νμ΄ν(duck typing)μ΄ μλ λͺ
λͺ©μ νμ΄ν(nominally typing) μ¬μ©
- λΌμ΄λΈλ¬λ¦¬λ₯Ό 곡κ°ν λ νμ
- νμ
μ€ν¬λ¦½νΈμ λ€λ₯Έ νμ
κ³Ό λ¬λ¦¬ ꡬ쑰μ νμ΄ν(duck typing)μ΄ μλ λͺ
λͺ©μ νμ΄ν(nominally typing) μ¬μ©
enum Flavor {
Vanilla = "Vanilla",
Chocolate = "Chocolate",
Strawberry = "Strawberry",
}
let flavor: Flavor = Flavor.Chocolate;
console.log(flavor); // μΆλ ₯κ°: "Chocolate"
- μλ°μ€ν¬λ¦½νΈμ νμ μ€ν¬λ¦½νΈμ λμ μ°¨μ΄
function scoop(flavor: Flavor) {
/* ... */
}
// μλ°μ€ν¬λ¦½νΈ
scoop("vanilla");
// νμ
μ€ν¬λ¦½νΈ: μ΄κ±°νμ μν¬νΈνκ³ λ¬Έμμ΄ λμ μ¬μ©ν΄μΌ νλ€
import { Flavor } from "ice-cream";
scoop(Flavor.VANILLA);
- JSμ TSμ λμμ΄ λ€λ₯΄κΈ° λλ¬Έμ λ¬Έμμ΄ μ΄κ±°νμ μ¬μ©νμ§ μλ κ²μ΄ μ’λ€!
- 리ν°λ΄ μ λμ¨μ μ¬μ©νμ: μμ νκ³ , JSμ νΈνλκ³ , μλμμ± κΈ°λ₯μ μ¬μ©ν μ μλ€.
type Flavor = "vanilla" | "chocolate" | "strawberry";
let flavor: Flavor = "vanilla";
맀κ°λ³μ μμ±
- ν΄λμ€λ₯Ό μ΄κΈ°νν λ, μμ± ν λΉμ μν΄ μμ±μμ 맀κ°λ³μ μ¬μ©
// JS
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
// TS
class Person {
constructor(public name: string) {} // λ©€λ² λ³μλ‘ nameμ μ μΈν κ²κ³Ό λμΌνκ² λμνλ€.
}
맀κ°λ³μ μμ±μ λ¬Έμ μ
- μΌλ°μ μΌλ‘ TS μ»΄νμΌμ νμ μ κ±°κ° μ΄λ€μ§λ―λ‘ μ½λκ° μ€μ΄λ€μ§λ§ => 맀κ°λ³μ μμ±μ μ½λκ° λμ΄λλ€
- 맀κ°λ³μ μμ±μ΄ λ°νμμλ μ¬μ©λμ§λ§ => TS κ΄μ μμ μ μ°λ κ²μ²λΌ 보μ
- 맀κ°λ³μ μμ±κ³Ό μΌλ° μμ±μ μμ΄μ μ¬μ© μ ν΄λμ€ μ€κ³κ° νΌλμ€λ½λ€
class Person {
first: string;
last: string;
constructor(public name: string) {
[this.first, this.last] = name.split(" ");
}
}
// Person ν΄λμ€μ first, last, name μμ±μ΄ μλ€.
// ν΄λμ€μ 맀κ°λ³μ μμ±λ§ μ‘΄μ¬νλ€λ©΄? ν΄λμ€ λμ μΈν°νμ΄μ€λ‘ λ§λ€κ³ κ°μ²΄ 리ν°λ΄μ μ¬μ©νμ
class PersonClass {
constructor(public name: string) {}
}
const p: PersonClass = { name: "Jed Bartlet" }; // 맀κ°λ³μ μμ± OK
interface Person {
name: string;
}
const jed: Person = new PersonClass("Jed Bartlet");
λ€μμ€νμ΄μ€μ νΈλ¦¬ν μ¬λμ μν¬νΈ (/// )
- ES6μ΄μ μλ JSμ 곡μμ λͺ¨λ μμ€ν
μ΄ μμλ€.
- Node.jsλ requiredμ module.exportsλ₯Ό μ¬μ©
- AMDλ define ν¨μμ μ½λ°±
- νμ
μ€ν¬λ¦½νΈ μμ μ체μ λͺ¨λ μμ€ν
: module ν€μλμ νΈλ¦¬ν μ¬λμ
- λͺ¨λ μμ€ν κ³Ό μΆ©λμ νΌνκΈ° μν΄μ module λμ namespace ν€μλλ₯Ό μΆκ°ν¨
- moduleκ³Ό /// μ¬μ©νμ§ μκΈ° => ES6μ λͺ¨λ μ¬μ©ν΄μΌ ν¨
module Foo
// index.ts
/// <reference path="other.ts"/>
foo.bar();
λ°μ½λ μ΄ν°
- ν΄λμ€, λ©μλ, μμ±μ annotation λΆμ΄κΈ°!
- μ΅κ·€λ¬ νλ μμν¬λ₯Ό μ§μνκΈ° μν΄ μΆκ°λμμλ€.
- tsconfig.jsonμ experimentalDecorators μ€μ νμ±ν νμ
- λ°μ½λ μ΄ν°λ νμ€νκ° μλ£λμ§ μμμ§λ§, νμ μ€ν¬λ¦½νΈμμ μ€νμ μΌλ‘ μ¬μ©ν μ μμ
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@logged // <-- λ°μ½λ μ΄ν°
greet() {
return `Hello, ${this.greeting}`;
}
}
function logged(originalFn: any, context: ClassMethodDecoratorContext) {
return function (this: any, ...args: any[]) {
console.log(`Calling ${String(context.name)}`);
return originalFn.call(this, ...args);
};
}
console.log(new Greeter("Dave").greet());
// μΆλ ₯ κ²°κ³Ό:
// Calling greet
// Hello, Dave
- ex) NestJSλ λ°μ½λ μ΄ν°λ₯Ό κ΄λ²μνκ² μ¬μ©νλ νλ μμν¬ => λͺ¨λ, 컨νΈλ‘€λ¬, μλΉμ€ λ±μ μ μν λ λ°μ½λ μ΄ν° μ¬μ©
Things to Remember
- By and large, you can convert TypeScript to JavaScript by removing all the types from your code.
- Enums, parameter properties, triple-slash imports, experimental decorators, and member visibility modifiers are historical exceptions to this rule.
- μΌλ°μ μΌλ‘ νμ μ 보λ₯Ό μ κ±°νλ©΄ μλ°μ€ν¬λ¦½νΈκ° λμ§λ§ μ΄κ±°ν, 맀κ°λ³μ μμ±, νΈλ¦¬ν μ¬λμ μν¬νΈ, λ°μ½λ μ΄ν°μ κ°μ΄ νμ μ 보λ₯Ό μ κ±°νλ€κ³ μλ°μ€ν¬λ¦½νΈκ° λμ§λ μλ κ²½μ°κ° μλ€.
- To keep TypeScriptβs role in your codebase as clear as possible and to avoid future compatibility issues, avoid nonstandard features.
- μμ λμ΄ν κΈ°λ₯ => νμ μ€ν¬λ¦½νΈμ μν μ λͺ ννκ² νκΈ° μν΄ μ¬μ©νμ§ μλ κ²μ΄ μ’λ€.