2024-05-01.md
π‘DIL: μ΄νν°λΈ νμ μ€ν¬λ¦½νΈ
μ€ν°λ: μκ° CS, https://github.com/monthly-cs/2024-05-effective-typescript
μμ±μΌ: 2024-05-01
μμ±μ: dusunax
μμ΄ν 1: νμ μ€ν¬λ¦½νΈμ μλ°μ€ν¬λ¦½νΈμ κ΄κ³ μ΄ν΄νκΈ° - TS vs JS
- "νμ μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈμ superset(μμμ§ν©)μ΄λ€."
- "νμ μ€ν¬λ¦½νΈλ νμ μ΄ μ μλ μλ°μ€ν¬λ¦½νΈμ superset(μμμ§ν©)μ΄λ€."
Superset
- νμ
μ€ν¬λ¦½νΈλ λ¬Έλ²μ μΌλ‘λ μλ°μ€ν¬λ¦½νΈμ μμμ§ν©μ΄λ€.
- JSμ λ¬Έλ² μ€λ₯ x => μ ν¨ν νμ μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨
- JSμ μ΄μ => λ¬Έλ² μ€λ₯κ° μλλλΌλ, νμ 체컀μκ² μ§μ
- λ¬Έλ²μ μ ν¨μ±κ³Ό λμ μ΄μλ λ 립μ λ¬Έμ
λΆλΆμ§ν©
- λͺ¨λ μλ°μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μ νμ
μ€ν¬λ¦½νΈλ€ => O
- JS -> TS λ§μ΄κ·Έλ μ΄μ μ μ΄μ
- λͺ¨λ νμ
μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μ μλ°μ€ν¬λ¦½νΈλ€ => X
- νμ μ λͺ μνλ μΆκ°μ μΈ λ¬Έλ²μ κ°μ§κΈ° λλ¬Έ
μλ°μ€ν¬λ¦½νΈμ νμ 체컀
let city = "new york city";
console.log(city.toUppercase());
// ~~~~~~~~~~~ Property 'toUppercase' does not exist on type
// 'string'. Did you mean 'toUpperCase'?
- νμ 체컀λ νμ μΆλ‘ μ ν΅ν΄, μλ°μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μμλ μ μ©νκ² μ€λ₯λ₯Ό μ°ΎμλΌ μ μλ€.
- λ°νμμ μ€λ₯λ₯Ό λ°μμν¬ μ½λ μ°ΎκΈ°: μ μ νμ μμ€ν
const states = [
{ name: "Alabama", capital: "Montgomery" },
{ name: "Alaska", capital: "Juneau" },
{ name: "Arizona", capital: "Phoenix" },
// ...
];
for (const state of states) {
console.log(state.capitol);
// ~~~~~~~ Property 'capitol' does not exist on type
// '{ name: string; capital: string; }'.
// Did you mean 'capital'?
// μ€νλ₯Ό μ°Ύμ μ μμ§λ§, μ΄λ μͺ½μ΄ μ€νμΈμ§ μ μ μλ€.
}
// λͺ
μμ μΌλ‘ States μ μΈ μ
interface State {
name: string;
capital: string;
}
const states: State[] = [
// νμ
ꡬ문μ μΆκ°νλ©΄ μ€λ₯λ₯Ό μ°Ύμ μ μλ€
{ name: "Alabama", capital: "Montgomery" },
{ name: "Alaska", capital: "Juneau" },
{ name: "Arizona", capitol: "Phoenix" },
// ~~~~~~~~~~~~~~~~~~ Object literal may only specify known
// properties, but 'capitol' does not exist in type
// 'State'. Did you mean to write 'capital'?
// ...
];
for (const state of states) {
console.log(state.capital);
}
νμ 체컀λ₯Ό ν΅κ³Όν νμ μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨
- νμ μμ±νλ νμ μ€ν¬λ¦½νΈ μ½λ = μ€λ₯κ° μλ μνμ, νμ 체컀λ₯Ό ν΅κ³Όν νλ‘κ·Έλ¨
- νμ μ€ν¬λ¦½νΈμ νμ μμ€ν μ μλ°μ€ν¬λ¦½νΈμ λ°νμ λμμ 'λͺ¨λΈλ§' νλ€.
- λν, μλμΉ μμ μ½λκ° μ€λ₯λ‘ μ΄μ΄μ§ μ μλ κ²½μ°λ₯Ό κ³ λ €ν¨
const x = 2 + "3"; // OK, type is string
const y = "2" + 3; // OK, type is string
- μλ°μ€ν¬λ¦½νΈμμλ μ€λ₯κ° λ°μνμ§ μμΌλ, νμ 체컀λ₯Ό λ¬Έμ μ μ νμνλ€.
const a = null + 7; // Evaluates to 7 in JS
// ~~~~ Operator '+' cannot be applied to types ...
const b = [] + 12; // Evaluates to '12' in JS
// ~~~~~~~ Operator '+' cannot be applied to types ...
alert("Hello", "TypeScript"); // alerts "Hello"
// ~~~~~~~~~~~~ Expected 0-1 arguments, but got 2
- νμ μ€ν¬λ¦½νΈκ° μ΄ν΄νλ κ°μ νμ κ³Ό μ€μ κ°μ μ°¨μ΄κ° μκΈ° λλ¬Έμ μ€λ₯κ° λ°μνμ§ μλ μ½λ
const names = ["Alice", "Bob"];
console.log(names[2].toUpperCase());
- νμ
μμ€ν
μ μ μ νμ
μ μ νμ±μ 보μ₯νλ κ²μ΄ λͺ©μ μ΄ μλ
- μ νμ± λ³΄μ₯ λͺ©μ μ΄λΌλ©΄? Reason, Elm κ°μ μΈμ΄λ₯Ό μ ννλ€. (λ°νμ μμ μ±μ 보μ₯νλ λμ , JSμ Supersetμ΄ μλλ―λ‘ λ§μ΄κ·Έλ μ΄μ κ³Όμ μ΄ λ³΅μ‘νλ€.)
βοΈ μμ½
- νμ
μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈμ Supersetμ΄λ€.
- λͺ¨λ μλ°μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μ μ΄λ―Έ νμ μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μ΄λ€. λ°λλ‘, νμ μ€ν¬λ¦½νΈλ λ³λμ λ¬Έλ²μ κ°μ§κ³ μκΈ° λλ¬Έμ μΌλ°μ μΌλ‘λ μ ν¨ν μλ°μ€ν¬λ¦½νΈ νλ‘κ·Έλ¨μ΄ μλλ€.
- νμ
μ€ν¬λ¦½νΈλ μλ°μ€ν¬λ¦½νΈ λ°νμ λμμ λͺ¨λΈλ§νλ νμ
μμ€ν
μ κ°μ§κ³ μλ€.
- λ°νμ μ€λ₯λ₯Ό λ°μμν€λ μ½λλ₯Ό μ°Ύμλ΄λ € νλ€.
- λ€λ§, νμ 체컀λ₯Ό ν΅κ³Όνλ©΄μλ λ°νμ μ€λ₯λ₯Ό λ°μν€λ μ½λλ μΆ©λΆν μ‘΄μ¬νλ€.
- νμ
μ€ν¬λ¦½νΈ νμ
μμ€ν
μ μ λ°μ μΌλ‘ μλ°μ€ν¬λ¦½νΈ λμμ λͺ¨λΈλ§νλ€.
- κ·Έλ¬λ, μλͺ»λ 맀κ°λ³μ κ°μμ κ°μ΄ μλ°μ€ν¬λ¦½νΈμμλ νμ©λμ§λ§ νμ μ€ν¬λ¦½νΈμμλ λ¬Έμ κ° λλ κ²½μ°κ° μμΌλ©° μ격ν¨μ μ¨μ ν μ·¨ν₯μ μ°¨μ΄λ€.
μμ΄ν 2: νμ μ€ν¬λ¦½νΈ μ€μ μ΄ν΄νκΈ° - Which TS
- μ€μ
- cli
tsc --noImplicitAny program.ts
- tsconfig.json
{"compilerOptions": {"noImplicitAny": true}}
- cli
- tsconfig.jsonμ μ¬μ©νλ κ² μ’μ μ΄μ
- νμ μ€ν¬λ¦½νΈλ₯Ό μ΄λ»κ² μ¬μ©ν κ³νμΈμ§, λλ£λ λ€λ₯Έ λκ΅¬κ° μ μ μλ€
- tsc --init
{"strict": true}
μ€μ μ ν΅ν΄, λλΆλΆμ μ€λ₯λ₯Ό μ‘μλΌ μ μλ€.- νμ μ€ν¬λ¦½νΈ μ½λλ₯Ό 곡μ νμ λ νμ 체컀 κ²°κ³Όκ° λ€λ₯΄λ€λ©΄, λμΌν μ»΄νμΌλ¬ μ€μ μ κ°κ³ μλμ§ νμΈν΄μΌ νλ€.
implicitAny
- μμμ μΈ, μ묡μ μΌλ‘ ν©μλ any
- trueλ‘ νμ λͺ μνλλ‘ ν΄μΌ νλ μ΄μ : νμ μ€ν¬λ¦½νΈκ° λ¬Έμ λ₯Ό λ°κ²¬νκΈ° μμνκ³ , μ½λμ κ°λ μ±μ΄ μ’μμ§λ©°, κ°λ°μμ μμ°μ±μ΄ ν₯μ
- falseλ‘ λλ κ²½μ°: λ§μ΄κ·Έλ μ΄μ λ± μΌλΆ κ²½μ°
// β
tsConfig: {"noImplicitAny":false}
function add(a, b) {
return a + b;
}
// β tsConfig: {"noImplicitAny":true}
function add(a, b) {
// ~ Parameter 'a' implicitly has an 'any' type
// ~ Parameter 'b' implicitly has an 'any' type
return a + b;
}
// (νμ
λͺ
μ) tsConfig: {"noImplicitAny":true}
function add(a: number, b: number) {
return a + b;
}
strictNullChecks
- nullκ³Ό undefinedκ° λͺ¨λ νμ
μμ νμ©λλκ°
- trueλ‘ null 체ν¬ν΄μΌ νλ μ΄μ : 'undefinedλ κ°μ²΄κ° μλλλ€'μ κ°μ λ°νμ μ€λ₯κ° λ°μν μ μλ€.
- falseλ‘ λλ κ²½μ°: λ§μ΄κ·Έλ μ΄μ λ± μΌλΆ κ²½μ°
- strictNullChecksκ° trueμ΄λ €λ©΄, noImplicitAnyκ° λ¨Όμ trueμ¬μΌ ν¨
// tsConfig: {"noImplicitAny":true,"strictNullChecks":false}
const x: number = null; // OK, null is a valid number\
// tsConfig: {"noImplicitAny":true,"strictNullChecks":true}
const x: number = null;
// ~ Type 'null' is not assignable to type 'number'
const x: number | null = null; // null νμ©μ λͺ
μμ μΌλ‘ λλ¬λ
- null μ²΄ν¬ μ½λ or λ¨μΈ
// tsConfig: {"noImplicitAny":true,"strictNullChecks":true}
const el = document.getElementById("status");
el.textContent = "Ready";
// ~~ Object is possibly 'null'
if (el) {
// (1) null 체ν¬νκΈ°
el.textContent = "Ready"; // OK, null has been excluded
}
// assertion, λ¨μΈ
el!.textContent = "Ready"; // OK, we've asserted that el is non-null
μμ½
- νμ μ€ν¬λ¦½νΈ μ»΄νμΌλ¬λ μΈμ΄μ ν΅μ¬ μμμ μν₯μ λ―ΈμΉλ μ€μ μ ν¬ν¨νλ€
- cli보λ€λ tsconfig.jsonμ μ¬μ©νλ κ²μ΄ μ’λ€
- μ격ν 체ν¬λ₯Ό μν΄ strict μ€μ μ κ³ λ €νλ€.
- noImplicitAnyμ strictNullChecksλ₯Ό μ€μ νλ κ²μ΄ μ’λ€.
μμ΄ν 3: μ½λ μμ±κ³Ό νμ μ΄ κ΄κ³μμμ μ΄ν΄νκΈ° - Independent
νμ μ€ν¬λ¦½νΈ μ»΄νμΌλ¬
- transpile νΈλμ€νμΌ: μ΅μ νμ μ€ν¬λ¦½νΈ, μλ°μ€ν¬λ¦½νΈλ₯Ό λΈλΌμ°μ μμ λμν μ μλλ‘ κ΅¬λ²μ μ μλ°μ€ν¬λ¦½νΈλ‘ νΈλμ€νμΌ νλ€.
- μ½λμ νμ μ€λ₯λ₯Ό 체ν¬νλ€.
Translateκ³Ό Compileμ΄ ν©μ³μ Έ νΈλμ€νμΌμ΄λΌλ μ μ‘°μ΄κ° νμν¨
μμ€μ½λλ₯Ό λμΌν λμμ νλ λ€λ₯Έ ννμ μμ€μ½λ (λ€λ₯Έ λ²μ , λ€λ₯Έ μΈμ΄)λ‘ λ³ννλ νμ. κ²°κ³Όλ¬Όμ΄ "μ»΄νμΌλμ΄μΌνλ μμ€μ½λ"μ΄κΈ° λλ¬Έμ μ»΄νμΌκ³Ό ꡬλΆνμ¬ λΆλ₯Έλ€.
- 1κ³Ό 2λ λ
립μ
- 1 => νμ
μ€ν¬λ¦½νΈκ° μλ°μ€ν¬λ¦½νΈλ‘ λ³νλ λ, μ½λ λ΄μ νμ
μλ μν₯μ μ£Όμ§ μλλ€. μλ°μ€ν¬λ¦½νΈμ μ€ν μμ μλ νμ
μ μν₯μ λ―ΈμΉμ§ μμ
- μ¦, νμ μ€λ₯κ° μλ μ½λλ μ»΄νμΌμ΄ κ°λ₯νλ€.
- 1 => νμ
μ€ν¬λ¦½νΈκ° μλ°μ€ν¬λ¦½νΈλ‘ λ³νλ λ, μ½λ λ΄μ νμ
μλ μν₯μ μ£Όμ§ μλλ€. μλ°μ€ν¬λ¦½νΈμ μ€ν μμ μλ νμ
μ μν₯μ λ―ΈμΉμ§ μμ
μ»΄νμΌ
- μ»΄νμΌμ νμ
체ν¬μ λ
립μ μΌλ‘ λμνκΈ° λλ¬Έμ, νμ
μ€λ₯κ° μλ μ½λλ μ»΄νμΌμ΄ κ°λ₯
- λ¬Έμ κ° λ λΆλΆμ μλ €μ£Όμ§λ§, λΉλλ₯Ό λ©μΆμ§λ μλλ€.
- μ€λ₯κ° μλλΌλ μ»΄νμΌλ μ°μΆλ¬Όμ΄ μλ€λ©΄, λ€λ₯Έ λΆλΆ ν μ€νΈ κ°λ₯
- μ€λ₯κ° μμ λ μ»΄νμΌνμ§ μμΌλ €λ©΄ tsconfig.jsonμ
{"noEmitOnError":true}
μ€μ
- λ¬Έμ κ° λ λΆλΆμ μλ €μ£Όμ§λ§, λΉλλ₯Ό λ©μΆμ§λ μλλ€.
μ»΄νμΌκ³Ό νμ 체ν¬
μ½λμ μ€λ₯κ° μμ λ, "μ»΄νμΌμ λ¬Έμ κ° μλ€"κ³ λ§νλ κ²½μ° κΈ°μ μ μΌλ‘ νλ¦°λ§!
- μλ°ν μ½λ μμ±λ§ μ»΄νμΌμ΄λΌκ³ ν μ μμ
- νμ μ€ν¬λ¦½νΈκ° μ ν¨ν μλ°μ€ν¬λ¦½νΈλΌλ©΄? μ»΄νμΌμ νλ€.
- κ·Έλ¬λ―λ‘, μ½λμ μ€λ₯κ° μλ€? "νμ 체ν¬μ λ¬Έμ κ° μλ€"κ³ λ§νλ κ²μ΄ μ ννλ€
λ°νμμλ νμ μ²΄ν¬ X
interface Square {
width: number;
}
interface Rectangle extends Square {
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
// βοΈ instanceofμ λ°νμμ μΌμ΄λμ§λ§ Ractangleμ νμ
μ΄λ€
// μΈν°νμ΄μ€, νμ
, νμ
ꡬ문μ JSλ‘ μ»΄νμΌλλ κ³Όμ μμ μ κ±°λλ€. => erasable
if (shape instanceof Rectangle) {
// ~~~~~~~~~ 'Rectangle' only refers to a type,
// but is being used as a value here
return shape.width * shape.height;
// ~~~~~~ Property 'height' does not exist
// on type 'Shape'
} else {
return shape.width * shape.width;
}
}
λ°νμμ νμ μ 보λ₯Ό μ μ§νλ λ°©λ² A: μμ± μ²΄ν¬
- attribute μμ± μ²΄ν¬λ λ°νμμλ§ μ κ·Ό κ°λ₯ν κ°μ κ΄λ ¨
- νμ 체컀λ shapeμ νμ μ Rectangleλ‘ λ³΄μ νκΈ° λλ¬Έμ μ€λ₯ μ¬λΌμ§
interface Square {
width: number;
}
interface Rectangle extends Square {
height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
// μμ± μ‘΄μ¬ μ¬λΆλ₯Ό 체ν¬~
if ("height" in shape) {
shape; // Type is Rectangle
return shape.width * shape.height;
} else {
shape; // Type is Square
return shape.width * shape.width;
}
}
λ°νμμ νμ μ 보λ₯Ό μ μ§νλ λ°©λ² B: Tagged Union
- λ°νμμ μ κ·Ό κ°λ₯ν νμ μ μ 보λ₯Ό λͺ μμ μΌλ‘ μ μ₯νλ νκ·Έ κΈ°λ²
interface Square {
// unionμ΄λκΉ ~ tagged union
kind: "square"; // λ°νμμ μ κ·Ό κ°λ₯ν tag
width: number;
}
interface Rectangle {
kind: "rectangle";
height: number;
width: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
if (shape.kind === "rectangle") {
// tag 체ν¬
shape; // Type is Rectangle
return shape.width * shape.height;
} else {
shape; // Type is Square
return shape.width * shape.width;
}
}
λ°νμμ νμ μ 보λ₯Ό μ μ§νλ λ°©λ² C: νμ μ ν΄λμ€λ‘ λ§λ€κΈ°
- μΈν°νμ΄μ€λ νμ μΌλ‘λ§ μ¬μ© κ°λ₯νμ§λ§, ν΄λμ€λ‘ μ μΈνλ©΄ νμ κ³Ό κ°μΌλ‘ λͺ¨λ μ¬μ©ν μ μλ€. (νμ κ³Ό κ°μΌλ‘ μ°Έμ‘°)
class Square {
constructor(public width: number) {}
}
class Rectangle extends Square {
constructor(public width: number, public height: number) {
super(width);
}
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
// νμ
μΌλ‘ μ°Έμ‘°
if (shape instanceof Rectangle) {
// κ°μΌλ‘ μ°Έμ‘°
shape; // Type is Rectangle
return shape.width * shape.height;
} else {
shape; // Type is Square
return shape.width * shape.width; // OK
}
}
νμ μ°μ°μ λ°νμμ μν₯μ μ£Όμ§ μλλ€.
// ts μ½λ
function asNumber(val: number | string): number {
return val as number; // νμ
λ¨μΈ
}
// λ³νλ js
function asNumber(val) {
return val; // νμ
λ¨μΈμ νμ
μ°μ°μ΄κ³ , λ°νμ λμμ μλ¬΄λ° μν₯μ λ―ΈμΉμ§ μμΌλ―λ‘, κ°μ μ μ νκΈ° μν΄μλ λ°νμμ νμ
μ 체ν¬νκ³ , μλ°μ€ν¬λ¦½νΈ μ°μ°μ ν΅ν΄ λ³νμ μνν΄μΌ νλ€.
}
λ°νμ νμ μ μ μΈλ νμ κ³Ό λ€λ₯Ό μ μλ€.
- νμ
μ€ν¬λ¦½νΈλ Dead Code μ£½μ μ½λλ₯Ό μ°ΎμλΌ μ μλ€.
- μμΈ: api response
νμ μ€ν¬λ¦½νΈ νμ μΌλ‘λ ν¨μλ₯Ό μ€λ²λ‘λ ν μ μλ€.
- C++ κ°μ μΈμ΄μμλ ν¨μ μ€λ²λ‘λ©μ΄ κ°λ₯ν¨
- νμ μ€ν¬λ¦½νΈλ μ€λ²λ‘λ© μλ¨
function add(a: number, b: number) {
// ~~~ Duplicate function implementation
return a + b;
}
function add(a: string, b: string) {
// ~~~ Duplicate function implementation
return a + b;
}
{"noImplicitAny":false}
, νμ μ€ν¬λ¦½νΈλ ν¨μ μ€λ²λ‘λ© κΈ°λ₯μ΄ μμ§λ§ νμ μμ€μμλ§ λμ- μ μΈλ¬Έμ΄ μ¬λ¬κ°μ¬λ implementation (ꡬν체)λ μ€μ§ νλ
// tsConfig: {"noImplicitAny":false}
function add(a: number, b: number): number; // μκ° μ§μ§
function add(a: string, b: string): string;
function add(a, b) {
return a + b;
}
const three = add(1, 2); // Type is number
const twelve = add("1", "2"); // Type is string
νμ μ€ν¬λ¦½νΈ νμ μ λ°νμ μ±λ₯μ μν₯μ μ£Όμ§ μλλ€.
- νμ κ³Ό νμ μ°μ°μλ JS λ³ν μμ μ μ κ±°λκΈ° λλ¬Έμ, λ°νμ μ±λ₯μ μν₯μ μ£Όμ§ μλλ€.
- νμ μ€ν¬λ¦½νΈμ μ μ νμ μ λΉμ©μ΄ μ ν λ€μ§ μλλ€.
νΉμ§
- 'λ°νμ' μ€λ²ν€λκ° μλ λμ , νμ
μ€ν¬λ¦½νΈ μ»΄νμΌλ¬λ 'λΉλνμ' μ€λ²ν€λκ° μλ€.
- μ»΄νμΌ μλκ° λΉ λ¦: μ¦λΆ(incremental) λΉλμ 체κ°
- μ€λ²ν€λκ° μ»€μ§λ©΄, λΉλ λꡬμμ transpile only μ€μ (νμ 체ν¬λ₯Ό 건λλ)
- νΈνμ±κ³Ό μ±λ₯ μ¬μ΄μ μ ν
- νΈνμ±: μ€λλ λ°νμ νκ²½μ μ§μνκΈ° μν΄ μ±λ₯ μ€λ²ν€λ κ°μ
- ex) ES5 νκΉμΌλ‘ μ»΄νμΌ μ, νΉμ ν¬νΌ μ½λ μΆκ°
- μ±λ₯: μ±λ₯ μ€μ¬μ λ€μ΄ν°λΈ ꡬν체 μ ν
- νΈνμ±: μ€λλ λ°νμ νκ²½μ μ§μνκΈ° μν΄ μ±λ₯ μ€λ²ν€λ κ°μ
μμ½
- μ½λ μμ±μ νμ
μμ€ν
κ³Ό 무κ΄νλ€.
- λ°νμ λμμ΄λ μ±λ₯μ μν₯μ μ£Όμ§ μλλ€.
- νμ μ€λ₯κ° μ‘΄μ¬νλλΌλ μ½λ μμ±(μ»΄νμΌ) κ°λ₯ (noEmitOnErrorλ‘ λ§κΈ° κ°λ₯)
- νμ
μ λ°νμμ μ¬μ©ν μ μλ€.
- λ°νμμ νμ
μ μ§μ νλ €λ©΄, νμ
μ 보 μ μ§λ₯Ό μν λ³λμ λ°©λ²
- ex) νκ·Έλ μ λμ¨, μμ± μ²΄ν¬, ν΄λμ€
- λ°νμμ νμ
μ μ§μ νλ €λ©΄, νμ
μ 보 μ μ§λ₯Ό μν λ³λμ λ°©λ²
μμ΄ν 4: ꡬ쑰μ νμ΄ν μ΅μν΄μ§κΈ° - Stuctural
- μλ°μ€ν¬λ¦½νΈλ duck typing
- ν¨μμ 맀κ°λ³μ κ°μ΄ λͺ¨λ μ λλ‘ μ£Όμ΄μ§λ€λ©΄, κ°μ΄ μ΄λ»κ² λ§λ€μ΄μ‘λμ§ μ κ²½μ°μ§ μκ³ μ¬μ©
- νμ
μ€ν¬λ¦½νΈλ λμ, 맀κ°λ³μ κ°μ΄ μꡬμ¬νμ λ§μ‘±νλ€λ©΄ νμ
μ΄ λ¬΄μμΈμ§ μ κ²½μ°μ§ μλ λμμ κ·Έλλ‘ λͺ¨λΈλ§νλ€.
- λ€λ§, νμ
체컀μ νμ
μ λν μ΄ν΄λκ° μ¬λκ³Ό λ€λ¦
- ꡬ쑰μ νμ΄νμ μ λλ‘ μ΄ν΄νλ€λ©΄ μ€λ₯μ μ€λ₯κ° μλ κ²½μ°μ μ°¨μ΄λ₯Ό μκ³ κ²¬κ³ ν μ½λ μμ± κ°λ₯
- λ€λ§, νμ
체컀μ νμ
μ λν μ΄ν΄λκ° μ¬λκ³Ό λ€λ¦
interface Vector2D {
x: number;
y: number;
}
function calculateLength(v: Vector2D) {
return Math.sqrt(v.x * v.x + v.y * v.y);
}
interface NamedVector {
name: string;
x: number;
y: number;
}
const v: NamedVector = { x: 3, y: 4, name: "Zee" };
calculateLength(v); // OK, result is 5
// Vector2D, NamedVector μ¬μ΄μ κ΄κ³λ₯Ό μ μΈνμ§ μμλ€
// NamedVectorκ° Vector2Dμ νΈνλλ ꡬ쑰μ΄κΈ° λλ¬Έμ, calculateLength νΈμΆμ΄ κ°λ₯νλ€ => ꡬ쑰μ νμ΄ν
ꡬ쑰μ νμ΄νμΌλ‘ λ°μν λ¬Έμ κ²½μ°
interface Vector2D {
x: number;
y: number;
}
function calculateLength(v: Vector2D) {
return Math.sqrt(v.x * v.x + v.y * v.y);
}
interface NamedVector {
name: string;
x: number;
y: number;
}
interface Vector3D {
x: number;
y: number;
z: number;
}
function normalize(v: Vector3D) {
const length = calculateLength(v); // 2D벑ν°λ₯Ό λ°λλ‘ μ μΈλ ν¨μμμ 3D벑ν°λ₯Ό λ°μμμλ νμ
μ²΄μ»€κ° λ¬Έμ λ‘ μΈμνμ§ μμ
return {
x: v.x / length,
y: v.y / length,
z: v.z / length,
};
} // κ²°κ΅ 1λ‘ normalizeλμ§ μμλ€.
νμ μ€ν¬λ¦½νΈμ νμ μμ€ν μ μ΄λ € μλ€(open)
- νμ μ΄ νμ₯μ μ΄λ €μλ€. νμ μ μ μΈλ μμ± μΈμ μμμ μμ±μ μΆκ°νλλΌλ μ€λ₯κ° λ°μνμ§ μλλ€.
function calculateLengthL1(v: Vector3D) {
let length = 0;
for (const axis of Object.keys(v)) {
const coord = v[axis];
// ~~~~~~~ Element implicitly has an 'any' type because ...
// 'string' can't be used to index type 'Vector3D'
length += Math.abs(coord);
}
return length;
}
const vec3D = { x: 3, y: 4, z: 1, address: "123 Broadway" };
// μ«μ μλ κ°μ λ£μ΄λ, νΈνλλ κ΅¬μ‘°λ‘ ν¨μ μ€ν (ꡬ쑰μ νμ΄ν)
calculateLengthL1(vec3D); // OK, returns NaN
- vλ μ΄λ€ μμ±μ΄λ μ§ κ°μ§ μ μκΈ° λλ¬Έμ, v[axis]κ° numberλΌκ³ νμ ν μ μλ€.
- μ νν νμ μΌλ‘ κ°μ²΄λ₯Ό μννκΈ° κΉλ€λ‘μ
function calculateLengthL1(v: Vector3D) {
return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z); // 루νλ³΄λ€ μ΄κ² λμ (μμ΄ν
54μμ λ€λ£° κ²)
}
ν΄λμ€μ κ²½μ°
class C {
foo: string;
constructor(foo: string) {
this.foo = foo;
}
}
const c = new C("instance of C");
const d: C = { foo: "object literal" }; // OK!
// ꡬ쑰μ μΌλ‘, νμν μμ±κ³Ό μμ±μκ° μ‘΄μ¬νκΈ° λλ¬Έμ λ¬Έμ κ° μλ€
// λ¨μ ν λΉμ΄ μλ μ°μ° λ‘μ§μ΄ μ‘΄μ¬νλ€λ©΄? dμ κ²½μ° μμ±μλ₯Ό μ€ννμ§ μμΌλ―λ‘ λ¬Έμ κ° λ°μνκ² λλ€.
// 1οΈβ£ λ©μλκ° μμ κ²½μ°, ꡬ쑰μ μΌλ‘ λ€λ¦
const d: C = { foo: "object literal" };
// ~ Property 'hi' is missing in type '{ foo: string; }' but required in type 'C'.
// 2οΈβ£ λ¨μ ν λΉμ΄ μλλΌ λ¬Έμ κ° μκΈ°λ κ²½μ°
class C {
foo: string;
constructor(foo: string) {
this.foo = foo + " with bar";
}
}
const c = new C("instance of C");
const d: C = { foo: "object literal" };
console.log(c.foo); // "instance of C with bar"
console.log(d.foo); // "object literal"
ν μ€νΈλ₯Ό μμ±ν λ
- DBμ 쿼리νκ³ κ²°κ³Όλ₯Ό μ²λ¦¬νλ ν¨μ
interface PostgresDB {
runQuery: (sql: string) => any[];
}
interface Author {
first: string;
last: string;
}
interface DB {
runQuery: (sql: string) => any[];
}
function getAuthors(database: DB): Author[] {
const authorRows = database.runQuery(`SELECT FIRST, LAST FROM AUTHORS`);
return authorRows.map((row) => ({ first: row[0], last: row[1] }));
}
// test μ μ©μ΄
// μΆμν(DB)ν΄μ, λ‘μ§κ³Ό ν
μ€νΈλ₯Ό νΉμ ν ꡬν(PostgresDB)μΌλ‘λΆν° λΆλ¦¬ν¨
// λΌμ΄λΈλ¬λ¦¬ κ°μ μμ‘΄μ±μ μλ²½ν λΆλ¦¬ν μ μλ€
test("getAuthors", () => {
const authors = getAuthors({
runQuery(sql: string) {
return [
["Toni", "Morrison"],
["Maya", "Angelou"],
];
},
});
expect(authors).toEqual([
{ first: "Toni", last: "Morrison" },
{ first: "Maya", last: "Angelou" },
]);
});
μμ½
- μλ°μ€ν¬λ¦½νΈλ λ νμ΄ν κΈ°λ°
- νμ μ€ν¬λ¦½νΈκ° μ΄λ₯Ό λͺ¨λΈλ§νκΈ° μν΄ κ΅¬μ‘°μ νμ΄νμ μ¬μ©ν¨μ μ΄ν΄ν΄μΌ νλ€.
- μ΄λ€ μΈν°νμ΄μ€μ ν λΉ κ°λ₯ν κ°μ΄λΌλ©΄, νμ μ μΈμ λͺ μμ μΌλ‘ λμ΄λ μμ±λ€μ κ°μ§κ³ μμ κ². (ꡬ쑰μ μΌλ‘ νΈν)
- νμ μ λ΄μΈλμ΄μμ§ μλ€
- ν΄λμ€λ ꡬ쑰μ νμ΄ν κ·μΉμ λ°λ₯Έλ€. μΈμ€ν΄μ€κ° μμκ³Ό λ€λ₯Ό μ μλ€.
- ꡬ쑰μ νμ΄νμ μ¬μ©νλ©΄ μ λ ν μ€ν μ΄ μ©μ΄νλ€.
μμ΄ν 5: any νμ μ§μνκΈ° - Any
- νμ
μ€ν¬λ¦½νΈμ νμ
μμ€ν
μ μ μ§μ (gradual)μ΄κ³ μ νμ (optional)
- μ μ§μ : μ½λμ νμ μ μ‘°κΈμ© μΆκ°ν μ μλ€.
- μ νμ : μΈμ λ μ§ νμ 체컀λ₯Ό ν΄μ ν μ μλ€.
let age: number;
age = "12";
// ~~~ Type '"12"' is not assignable to type 'number'
age = "12" as any; // OK
any νμ μλ νμ μμ μ±μ΄ μλ€.
- νΌλ!
let age: number;
age = "12" as any;
age += 1; // OK; at runtime, age is now "121"
anyλ ν¨μ contract (μκ·Έλμ²)λ₯Ό 무μνλ€.
- ν¨μ
- νΈμΆνλ μͺ½μ μ½μλ νμ μ μ λ ₯μ μ 곡
- ν¨μλ μ½μλ νμ μ μΆλ ₯ λ°ν
function calculateAge(birthDate: Date): number {
// birthDateλ Date μλ!
...
}
let birthDate: any = "1990-01-19";
calculateAge(birthDate); // OK
- μλ°μ€ν¬λ¦½νΈλ μ’ μ’ μμμ μΌλ‘ νμ μ΄ λ³νλκΈ° λλ¬Έμ, string νμ μ΄ number νμ μ΄ νμν κ³³μμ μ€λ₯ μμ΄ μ€νλ λκ° μκ³ , κ·Έλ΄ κ²½μ° λ¬Έμ λ°μ
any νμ μλ μΈμ΄ μλΉμ€κ° μ μ©λμ§ μλλ€.
- νμ
μ€ν¬λ¦½νΈ μΈμ΄ μλΉμ€
- μ¬λ²μ νμ μ΄ μλ€λ©΄, μλμμ±κ³Ό μ μ ν λμλ§μ΄ μ 곡λ¨
- Rename Symbol μλ¨
- νμ μ€ν¬λ¦½νΈμ λͺ¨ν "νμ₯ κ°λ₯ν μλ°μ€ν¬λ¦½νΈ": μμ°μ± ν₯μ!
any νμ μ μ½λ 리ν©ν°λ§ λ λ²κ·Έλ₯Ό κ°μΆλ€.
- νμ
체컀λ₯Ό ν΅κ³Όν¨μλ, λ°νμμ μ€λ₯κ° λ°μνλ€.
- anyκ° μλ ꡬ체μ μΈ νμ μ μ¬μ©νλ€λ©΄, μ€λ₯λ₯Ό λ°κ²¬νμ κ²
interface ComponentProps {
onSelectItem: (item: any) => void;
}
function renderSelector(props: ComponentProps) {
/* ... */
}
let selectedId: number = 0;
function handleSelectItem(item: any) {
selectedId = item.id;
}
renderSelector({ onSelectItem: handleSelectItem });
// ComponentPropsλ₯Ό μλ μ½λλ‘ λ°κΏ¨μ λ, handleSelectItemμ μ€λ₯λ₯Ό λ°νμκΉμ§ μμλ³Ό μ μλ€. νμ
체컀λ₯Ό ν΅κ³ΌνκΈ° λλ¬Έ.
interface ComponentProps {
onSelectItem: (id: number) => void;
}
anyλ νμ μ€κ³λ₯Ό κ°μΆλ€
- κ°μ²΄λ₯Ό μ μν λ νΉν λ¬Έμ ! μ°μ§λ§μΈμ
- μν κ°μ²΄μ μ€κ³λ₯Ό κ°μΆ°λ²λ¦¬κΈ° λλ¬Έ
- κΉλνκ³ μ ννκ³ λͺ
λ£ν μ½λ μμ±μ μν΄ νμ
μ€κ³λ νμλ€
- λλ£κ° μ½λ κ²ν μ, μ ν리μΌμ΄μ μ μνλ₯Ό μ΄λ»κ² λ³κ²½νλμ§ μ½λλΆν° μ¬κ΅¬μ±ν΄μΌ ν¨
- μ€κ³κ° λͺ νν 보μ΄λλ‘ νμ μ μμ±νμ
anyλ νμ μμ€ν μ μ λ’°λλ₯Ό λ¨μ΄λ¨λ¦°λ€
- νμ
μ²΄μ»€κ° ν΄λ¨Ό μλ¬λ₯Ό μ‘μμ£Όκ³ , μ½λμ μ λ’°λλ₯Ό λμ
- λ°νμμ νμ μ€λ₯κ° μκΈ΄λ€λ©΄? νμ 체컀λ₯Ό μ λ’°ν μ μμ κ²
- any νμ
μ΄ λ§λ€λ©΄? κ³¨μΉ μν
- νμ μ€λ₯ μμ
- μ€μ νμ κΈ°μ΅
μμ½
- any νμ μ μ¬μ©νλ©΄, νμ 체컀μ νμ μ€ν¬λ¦½νΈ μΈμ΄λ₯Ό 무λ ₯ν
- μ§μ§ λ¬Έμ μ μ κ°μΆκ³ , κ°λ° κ²½νμ μ νμν€κ³ , νμ μμ€ν μ μ λ’°λλ₯Ό λ¨μ΄λ¨λ¦°λ€.
- μ°μ§ λ§μΈμ~