LHS_EffectiveTypescript_08 - YDP-SPLOUNGE-CLUB/typescript-study GitHub Wiki
μμ΄ν 36 ν΄λΉ λΆμΌμ μ©μ΄λ‘ νμ μ΄λ¦ μ§κΈ°
κ°λ μ±μ λμ΄κ³ , μΆμν μμ€μ μ¬λ¦¬κΈ° μν΄μ ν΄λΉ λΆμΌμ μ©μ΄λ₯Ό μ¬μ©ν΄μΌ νλ€.
νμ μ΄λ¦ μ§κΈ° μμ νμ μ€κ³μ μ€μν λΆλΆμ΄λ€.
μμ λ νμ , μμ±, λ³μμ μ΄λ¦μ μλλ₯Ό λͺ νν νκ³ μ½λμ νμ μ μΆμν μμ€μ λμ¬μ€λ€.
λλ¬Όλ€μ λ°μ΄ν°λ² μ΄μ€λ₯Ό ꡬμΆνλ€κ³ κ°μ
interface Animal {
name: string
endangered: boolean
habitat: string
}
const leopard: Animal = {
name: 'Snow Leopard',
endangered: false,
habitat: 'tundra',
}
μ΄ μ½λμλ 4κ°μ§ λ¬Έμ κ° μλ€.
- name μ λ§€μ° μΌλ°μ μΈ μ©μ΄μ΄λ€. λλ¬Όμ νλͺ μΈμ§ μΌλ°μ μΈ λͺ μΉμΈμ§ μ μ μλ€.
- endangered μμ±μ΄ λ©Έμ’ μκΈ°λ₯Ό νννκΈ° μν΄ boolean νμ μ μ¬μ©ν κ²μ΄ μ΄μνλ€. μ΄λ―Έ λ©Έμ’ λ λλ¬Όμ trueλ‘ ν΄μΌ νλμ§ νλ¨ν μ μλ€. λ μλλ₯Ό 'λ©Έμ’ μκΈ° λλ λ©Έμ’ 'μΌλ‘ μκ°ν κ²μΌμ§λ λͺ¨λ₯Έλ€.
- μμμ§λ₯Ό λνλ΄λ habitat μμ±μ λ무 λ²μκ° λμ stringνμ μμ΄ν 33 string νμ λ³΄λ€ λ ꡬ체μ μΈ νμ μ¬μ©νκΈ°μΌ λΏλ§ μλλΌ μμμ§λΌλ λ» μ체λ λΆλΆλͺ νκΈ° λλ¬Έμ λ€λ₯Έ μμ±λ€λ³΄λ€λ ν¨μ¬ λͺ¨νΈνλ€.
- κ°μ²΄μ λ³μλͺ μ΄ leopard μ΄μ§λ§ name μμ±μ κ°μ 'Snow Leopard' μ΄λ€. κ°μ²΄μ μ΄λ¦κ³Ό μμ±μ nameμ΄ λ€λ₯Έ μλλ‘ μ¬μ©λ κ²μΈμ§ λΆλΆλͺ νλ€.
μ΄ μμ λ₯Ό ν΄κ²°νλ €λ©΄ μμ±μλ₯Ό μ°Ύμ μλλ₯Ό λ¬Όμ΄μΌ νλ€. κ·Έλ¬λ μμ±μκ° νμ¬μ μκ±°λ μ½λλ₯Ό κΈ°μ΅νμ§ λͺ»ν κ²μ΄λ€. λλ μμ±μκ° λ³ΈμΈμ΄λΌλ κ²μ μκ²λλ μ΅μ μ μν©μ΄ μ¬ μ μλ€....
λ€μ νμ μ μΈμ μλ―Έκ° λΆλͺ νλ€.
interface Animal {
commonName: string
genus: string
species: string
status: ConservationStatus
climates: KoppenClimate[]
}
type ConservationStatus = 'EX' | 'EW' | 'CR' | 'EN' | 'VU' | 'NT' | 'LC'
type KoppenClimate =
| 'Af' | 'Am' | 'As' | 'Aw' | 'BSh' | 'BSk' | 'BWh' | 'BWk'
| 'Cfa' | 'Cfb' | 'Cfc' | 'Csa' | 'Csb' | 'Csc' | 'Cwa' | 'Cwb'
| 'Cwc' | 'Dfa' | 'Dfb' | 'Dfc' | 'Dfd' | 'Dsa' | 'Dsb' | 'Dsc'
| 'Dwa' | 'Dwb' | 'Dwc' | 'Dwd' | 'EF' | 'ET'
const snowLeopard: Animal = {
commonName: 'Snow Leopard',
genus: 'Panthera',
species: 'Uncia',
status: 'VU', // μ·¨μ½μ’
(vulnerable)
climates: ['ET', 'EF', 'Dfd'], // κ³ μ°λ(alpine) or μκ³ μ°λ(subalpine)
}
λ€μ 3κ°μ§λ₯Ό κ°μ νλ€.
- nameμ commonName, genus, species λ± κ΅¬μ²΄μ μΈ μ©μ΄λ‘ λ체νλ€.
- endangerdλ λλ¬Ό λ³΄νΈ λ±κΈμ λν IUCNμ νμ€ λΆλ₯ 체κ³μΈ ConservationStatus νμ μ statusλ‘ λ³κ²½λμλ€.
- habitatμ κΈ°νλ₯Ό λ»νλ climatedsλ‘ λ³κ²½λμμΌλ©°, μΎ¨ν κΈ°ν λΆλ₯λ₯Ό μ¬μ©νλ€.
λ³κ²½λ μ½λλ λ°μ΄ν°λ₯Ό ν¨μ¬ λͺ ννκ² νννκ³ μλ€. κ·Έλ¦¬κ³ μ 보λ₯Ό μ°ΎκΈ° μν΄ μ¬λμ μμ‘΄ν νμκ° μλ€.
κ°μ μλ―Έμ λ€λ₯Έ μ΄λ¦μ λΆμ΄λ©΄ μλλ€. νΉλ³ν μλ―Έκ° μμλλ§ μ©μ΄λ₯Ό ꡬλΆν΄μΌ νλ€.
μ½λλ‘ νννκ³ μ νλ λͺ¨λ λ·΄μΌμλ μ£Όμ λ₯Ό μ€λͺ νκΈ° μν μ λ¬Έ μ©μ΄λ€μ΄ μλ€.
μ체μ μΌλ‘ μ©μ΄λ₯Ό λ§λ€μ§λ§κ³ μ΄λ―Έ μ‘΄μ¬νλ μ©μ΄λ₯Ό μ¬μ©νμ. μ΄λ° μ©μ΄λ€μ μλ , μμλ , μ μΈκΈ°μ κ±Έμ³ λ€λ¬μ΄μ Έ μμΌλ©° νμ₯μμ μ€μ λ‘ μ¬μ©λκ³ μμ κ²μ΄λ€.
μμ΄ν 37 곡μ λͺ μΉμλ μνλ₯Ό λΆμ΄κΈ°
νμ μ€ν¬λ¦½νΈλ ꡬ쑰μ νμ΄ν(λ νμ΄ν)μ μ¬μ©νκΈ° λλ¬Έμ, κ°μ μΈλ°νκ² κ΅¬λΆνμ§ λͺ»νλ κ²½μ°κ° μλ€. κ°μ ꡬλΆνκΈ° μν΄ κ³΅μ λͺ μΉμ΄ νμνλ€λ©΄ μνλ₯Ό λΆμ΄λ κ²μ κ³ λ €ν΄μΌ νλ€.
interface Vector2D {
x: number
y: number
}
function calculateNorm(p: Vector2D) {
return Math.sqrt(p.x * p.x + p.y * p.y)
}
calculateNorm({ x: 3, y: 4 }) // OK, result is 5
const vec3D = { x: 3, y: 4, z: 1 }
calculateNorm(vec3D) // OK! result is also 5
μ΄ μ½λλ ꡬ쑰μ νμ΄ν κ΄μ μμλ λ¬Έμ κ° μλ€. νμ§λ§ μνμ μΌλ‘ λ°μ§λ©΄ 2μ°¨μ 벑ν°λ₯Ό μ¬μ©ν΄μΌ μ΄μΉμ λ§λ€.
ν¨μκ° 3μ°¨μ 벑ν°λ₯Ό νμ©νμ§ μκ² νλ €λ©΄ 곡μ λͺ μΉμ μ¬μ©νλ©΄ λλ€.
곡μ λͺ μΉ κ°λ μ νμ μ€ν¬λ¦½νΈμμ νλ΄ λ΄λ €λ©΄ 'μν(brand)'λ₯Ό λΆμ΄λ©΄ λλ€.
interface Vector2D {
_brand: '2d'
x: number
y: number
}
function vec2D(x: number, y: number): Vector2D {
return { x, y, _brand: '2d' }
}
function calculateNorm(p: Vector2D) {
return Math.sqrt(p.x * p.x + p.y * p.y) // Same as before
}
calculateNorm(vec2D(3, 4)) // OK, returns 5
const vec3D = { x: 3, y: 4, z: 1 }
calculateNorm(vec3D)
// ~~~~~ Property '_brand' is missing in type...
μν κΈ°λ²μ νμ μμ€ν μμ λμνμ§λ§ λ°νμμ μνλ₯Ό κ²μ¬νλ κ²κ³Ό λμΌν ν¨κ³Όλ₯Ό μ»μ μ μλ€.
μλ₯Ό λ€μ΄, μ λ κ²½λ‘λ₯Ό μ¬μ©ν΄ νμΌ μμ€ν μ μ κ·Όνλ ν¨μλ₯Ό κ°μ ν΄λ³΄μ. λ°νμμλ μ λ κ²½λ‘('/')λ‘ μμνλμ§ μ²΄ν¬νκΈ° μ½μ§λ§, νμ μμ€ν μμλ μ λ κ²½λ‘λ₯Ό νλ¨νκΈ° μ΄λ ΅κΈ° λλ¬Έμ μν κΈ°λ²μ μ¬μ©νλ€.
type AbsolutePath = string & { _brand: 'abs' }
function listAbsolutePath(path: AbsolutePath) {
// ...
}
function isAbsolutePath(path: string): path is AbsolutePath {
return path.startsWith('/')
}
string νμ μ΄λ©΄μ _brand μμ±μ κ°μ§λ κ°μ²΄λ₯Ό λ§λ€ μλ μλ€. AbsolutePathλ μ¨μ ν νμ μμ€ν μ μμμ΄λ€.
λ§μ½ path κ°μ΄ μ λ κ²½λ‘μ μλ κ²½λ‘ λ λ€ λ μ μλ€λ©΄, νμ μ μ μ ν΄ μ£Όλ νμ κ°λλ₯Ό μ¬μ©ν΄μ μ€λ₯λ₯Ό λ°©μ§ν μ μλ€.
function f(path: string) {
if (isAbsolutePath(path)) {
listAbsolutePath(path)
} listAbsolutePath(path)
// ~~~~ Argument of type 'string' is not assignable
// to parameter of type 'AbsolutePath'}
μμ΄ν 38 any νμ μ κ°λ₯ν ν μ’μ λ²μμμλ§ μ¬μ©νκΈ°
μλμΉ μμ νμ μμ μ±μ μμ€μ νΌνκΈ° μν΄μ anyμ μ¬μ© λ²μλ₯Ό μ΅μνμΌλ‘ μ’νμΌ νλ€.
function f1() {
const x: any = expressionReturningFoo() // μ΄λ κ² νμ§ λ§μ.
processBar(x)
}
function f2() {
const x = expressionReturningFoo()
processBar(x as any) // μ΄κ² λ«λ€.
}
f2 ν¨μμ ννκ° κΆμ₯λλ μ΄μ λ processBar ν¨μμ λ§€κ°λ³μμλ§ μ¬μ©λ ννμμ΄λ―λ‘ λ€λ₯Έ μ½λμλ μν₯μ λ―ΈμΉμ§ μκΈ° λλ¬Έμ΄λ€
ν¨μμ λ°ν νμ μ΄ anyμΈ κ²½μ° νμ μμ μ±μ΄ λλΉ μ§λ€. λ°λΌμ anyνμ μ λ°ννλ©΄ μ λ μλλ€.
function f1() {
const x: any = expressionReturningFoo()
processBar(x)
return x
}
function g() {
const foo = f1() // Type is any
foo.fooMethod() // μ΄ ν¨μ νΈμΆμ 체ν¬λμ§ μμ΅λλ€
}
λ°ννμ μ anyλ‘ λ°ννλ©΄ κ·Έ μν₯λ ₯μ νλ‘μ νΈ μ λ°μ μ μΌλ³μ²λΌ νΌμ§κ²λλ€.
κ°μ λ‘ νμ μ€λ₯λ₯Ό μ κ±°νλ €λ©΄ any λμ @ts-ignore μ¬μ©νλ κ²μ΄ μ’λ€.
function f1() {
const x = expressionReturningFoo()
// @ts-ignore
processBar(x)
return x
}
@ts-ignoreλ₯Ό μ¬μ©ν λ€μ μ€μ μ€λ₯κ° λ¬΄μλλ€. νμ§λ§ κ·Όλ³Έμ μΈ μμΈμ ν΄κ²°ν κ²μ΄ μλκΈ° λλ¬Έμ λ€λ₯Έ κ³³μμ λ ν° λ¬Έμ κ° λ°μν μ μλ€.
μμ΄ν 39 anyλ₯Ό ꡬ체μ μΌλ‘ λ³νν΄μ μ¬μ©νκΈ°
anyλ₯Ό μ¬μ©ν λλ μ λ§λ‘ λͺ¨λ κ°μ΄ νμ©λμ΄μΌλ§ νλμ§ λ©΄λ°ν κ²ν ν΄μΌ νλ€.
μλ₯Ό λ€μ΄ any νμ μ κ°μ κ·Έλλ‘ μ κ·μμ΄λ ν¨μμ λ£λ κ²μ κΆμ₯λμ§ μλλ€.
function getLengthBad(array: any) {
// μ΄λ κ² νμ§ λ§μλ€!
return array.length
}
function getLength(array: any[]) {
return array.length
}
μμ μμ μμ anyλ₯Ό μ¬μ©νλ getLengthBad보λ€λ
any[]λ₯Ό μ¬μ©νλ getLengthκ° λ μ’μ ν¨μμ΄λ€
μ΄μ λ μΈκ°μ§λ€.
- ν¨μ λ΄μ array.length νμ μ΄ μ²΄ν¬λλ€.
- ν¨μμ λ°ν νμ μ΄ any λμ numberλ‘ μΆλ‘ λλ€.
- ν¨μ νΈμΆλ λ λ§€κ°λ³μκ° λ°°μ΄μΈμ§ 체ν¬λλ€.
μΈμλ₯Ό λ£μ΄ μ€νν΄λ³΄λ©΄ μ°¨μ΄μ μ μ μ μλ€.
anyλ³΄λ€ λ μ ννκ² λͺ¨λΈλ§ν μ μλλ‘any[]λλ{[id: string]: any}λλ () => any μ²λΌ ꡬ체μ μΈ ννλ₯Ό μ¬μ©ν΄μΌ νλ€.
ν¨μμ λ§€κ°λ³μκ° κ°μ²΄μ΄κΈ΄ νμ§λ§ κ°μ μ μ μλ€λ©΄ {[key: string]: any} μ²λΌ μ μΈνλ©΄ λλ€.
function hasTwelveLetterKey(o: { [key: string]: any }) {
for (const key in o) {
if (key.length === 12) {
return true
}
}
return false
}
μ μμ μ²λΌ λ§€κ°λ³μκ° κ°μ²΄μ§λ§ κ°μ μ μ μλ€λ©΄ ${[key: string]: any} λμ λͺ¨λ λΉκΈ°λ³Έν νμ
μ ν¬ν¨νλ object νμ
μ μ¬μ©ν μλ μλ€.
ν¨μμ νμ μλ λ¨μν anyλ₯Ό μ¬μ©ν΄μλ μλλ€. μ΅μνμΌλ‘λλ§ κ΅¬μ²΄νν μ μλ μΈ κ°μ§ λ°©λ²μ΄ μλ€.
type Fn0 = () => any // λ§€κ°λ³μ μμ΄ νΈμΆ κ°λ₯ν λͺ¨λ ν¨μ
type Fn1 = (arg: any) => any // λ§€κ°λ³μ νκ°
type FnN = (...args: any[]) => any // λͺ¨λ κ°μμ λ§€κ°λ³μ "Function" νμ
κ³Ό λμΌ
μμ μμ μ λ±μ₯ν μΈ κ°μ§ ν¨μ νμ
μ λͺ¨λ any 보λ€λ ꡬ체μ μ΄λ€.
λ§μ§λ§ μ€μ μ μ΄ν΄λ³΄λ©΄ ...argsμ νμ
μ any[]λ‘ μ μΈνλ€.
any λ‘ μ μΈν΄λ λμνμ§λ§ any[]λ‘ μ μΈνλ©΄ λ°°μ΄ ννλΌλ κ²μ μ μ μμ΄ λ ꡬ체μ μ΄λ€.
μμ΄ν 40 ν¨μ μμΌλ‘ νμ λ¨μΈλ¬Έ κ°μΆκΈ°
νμ μ μΈλ¬Έμ μΌλ°μ μΌλ‘ νμ μ μννκ² λ§λ€μ§λ§ μν©μ λ°λΌ νμνκΈ°λ νκ³ νμ€μ μΈ ν΄κ²°μ± μ΄ λκΈ°λ νλ€. λΆκ°νΌνκ² μ¬μ©ν΄μΌ νλ€λ©΄, μ νν μ μλ₯Ό κ°μ§λ ν¨μ μμΌλ‘ μ¨κΈ°λλ‘ νμ.
ν¨μλ₯Ό μμ±νλ€ λ³΄λ©΄, μΈλΆλ‘ λλ¬λ νμ μ μλ κ°λ¨νμ§λ§ λ΄λΆ λ‘μ§μ΄ 볡μ‘ν΄μ μμ ν νμ μΌλ‘ ꡬννκΈ° μ΄λ €μ΄ κ²½μ°κ° λ§λ€.
ν¨μμ λͺ¨λ λΆλΆμ μμ ν νμ μΌλ‘ ꡬννλ κ²μ΄ μ΄μμ μ΄μ§λ§, λΆνμν μμΈ μν©κΉμ§ κ³ λ €ν΄ κ°λ©° νμ μ 보λ₯Ό νλ€κ² ꡬμ±ν νμλ μλ€.
ν¨μ λ΄λΆμλ νμ λ¨μΈμ μ¬μ©νκ³ ν¨μ μΈλΆλ‘ λλ¬λλ νμ μ μλ₯Ό μ νν λͺ μνλ μ λλ‘ λλ΄λ κ²μ΄ λ«λ€.
νλ‘μ νΈ μ λ°μ μνν νμ λ¨μΈλ¬Έμ΄ λλ¬λμλ κ²λ³΄λ€, μ λλ‘ νμ μ΄ μ μλ ν¨μ μμΌλ‘ νμ λ¨μΈλ¬Έμ κ°μΆλ κ²μ΄ λ μ’μ μ€κ³μ΄λ€.
declare function shallowEqual(a: any, b: any): boolean
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
for (const [k, aVal] of Object.entries(a)) {
if (!(k in b) || aVal !== b[k]) {
// ~~~~ Element implicitly has an 'any' type
// because type '{}' has no index signature return false
}
}
return Object.keys(a).length === Object.keys(b).length
}
in ꡬ문μ k in b 체ν¬λ‘ b κ°μ²΄μ k μμ±μ΄ μλ€λ κ²μ νμΈνμ§λ§ b[k] λΆλΆμμ μ€λ₯κ° λ°μνλ κ²μ΄ μ΄μνλ€.
μ΄μ¨λ μ€μ μ€λ₯κ° μλλΌλ κ²μ μκ³ μκΈ° λλ¬Έμ anyλ‘ λ¨μΈνλ μ λ°μ μλ€.
declare function shallowEqual(a: any, b: any): boolean
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
for (const [k, aVal] of Object.entries(a)) {
if (!(k in b) || aVal !== (b as any)[k]) {
return false
}
}
return Object.keys(a).length === Object.keys(b).length
}
b as any νμ λ¨μΈλ¬Έμ μμ νλ©° (k in b 체ν¬λ₯Ό νμΌλ―λ‘), κ²°κ΅ μ νν νμ μΌλ‘ μ μλκ³ μ λλ‘ κ΅¬νλ ν¨μκ° λλ€. κ°μ²΄κ° κ°μμ§ μ²΄ν¬νκΈ° μν΄ κ°μ²΄ μνμ λ¨μΈλ¬Έμ΄ μ½λμ μ§μ λ€μ΄κ°λ κ²λ³΄λ€, μμ μ½λμ²λΌ λ³λμ ν¨μλ‘ λΆλ¦¬ν΄ λ΄λ κ²μ΄ ν¨μ¬ μ’μ μ€κ³μ΄λ€.