본문으둜 κ±΄λ„ˆλ›°κΈ°

🐀 Chapter 3: νƒ€μž… μΆ”λ‘ 

πŸ₯• μ•„μ΄ν…œ 19. μΆ”λ‘  κ°€λŠ₯ν•œ νƒ€μž…μ„ μ‚¬μš©ν•΄ μž₯ν™©ν•œ μ½”λ“œ λ°©μ§€ν•˜κΈ°β€‹

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ λ§Žμ€ νƒ€μž… ꡬ문은 사싀 λΆˆν•„μš”ν•©λ‹ˆλ‹€. λ‹€μŒκ³Ό 같이 μ½”λ“œμ˜ λͺ¨λ“  λ³€μˆ˜μ— νƒ€μž…μ„ μ„ μ–Έν•˜λŠ” 것은 비생산적이며 ν˜•νŽΈμ—†λŠ” μŠ€νƒ€μΌλ‘œ μ—¬κ²¨μ§‘λ‹ˆλ‹€.

let x: number = 12;

λ‹€μŒμ²˜λŸΌλ§Œ 해도 μΆ©λΆ„ν•©λ‹ˆλ‹€.

let x = 12;

νŽΈμ§‘κΈ°μ—μ„œ x에 마우슀λ₯Ό 올렀 보면, νƒ€μž…μ΄ number둜 이미 μΆ”λ‘ λ˜μ–΄ μžˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
νƒ€μž… 좔둠이 λœλ‹€λ©΄ λͺ…μ‹œμ  νƒ€μž… ꡬ문은 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 였히렀 λ°©ν•΄κ°€ 될 λΏμž…λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 더 λ³΅μž‘ν•œ 객체도 μΆ”λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const person = {
name: 'Sojourner Truth'.
born: {
where: 'Swartekill, NY',
when: 'c.1797',
},
died: {
where: 'Battle Creek, MI',
when: 'Nov, 26. 1883',
}
};

νƒ€μž…μ„ μƒλž΅ν•˜κ³  λ‹€μŒμ²˜λŸΌ μž‘μ„±ν•΄λ„ μΆ©λΆ„ν•©λ‹ˆλ‹€.

λ‹€μŒ 예제처럼 λ°°μ—΄μ˜ κ²½μš°λ„ 객체와 λ§ˆμ°¬κ°€μ§€μž…λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μž…λ ₯을 λ°›μ•„ 연산을 ν•˜λŠ” ν•¨μˆ˜κ°€ μ–΄λ–€ νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ”μ§€ μ •ν™•νžˆ μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€.

function square(nums: number[]) {
return nums.map(x => x * x);
}

const squares = square([1, 2, 3, 4]); // νƒ€μž…μ€ number[]

νƒ€μž…μ΄ μΆ”λ‘ λ˜λ©΄ λ¦¬νŒ©ν„°λ§ μ—­μ‹œ μš©μ΄ν•΄μ§‘λ‹ˆλ‹€. Product νƒ€μž…κ³Ό 기둝을 μœ„ν•œ ν•¨μˆ˜λ₯Ό κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface Product {
id: number;
name: string;
price: number;
}

function logProduct(product: Product) {
const id: number = product.id;
const name: string = product.name;
const price: number = product.price;
console.log(id, name, price);
}

그런데 id에 λ¬Έμžλ„ λ“€μ–΄ μžˆμ„ 수 μžˆμŒμ„ λ‚˜μ€‘μ— μ•Œκ²Œ λ˜μ—ˆλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ Product λ‚΄μ˜ id νƒ€μž…μ„ λ³€κ²½ν•©λ‹ˆλ‹€. 그러면 logProduct λ‚΄μ˜ id λ³€μˆ˜ 선언에 μžˆλŠ” νƒ€μž…κ³Ό λ§žμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

interface Product {
id: string;
name: string;
price: number;
}

function logProduct(product: Product) {
const id: number = product.id; // ~~ 'string' ν˜•μ‹μ€ 'number' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.
const name: string = product.name;
const price: number = product.price;
console.log(id, name, price);
}

logProduct ν•¨μˆ˜ λ‚΄μ˜ λͺ…μ‹œμ  νƒ€μž… ꡬ문이 μ—†μ—ˆλ‹€λ©΄, μ½”λ“œλŠ” μ•„λ¬΄λŸ° μˆ˜μ • 없이도 νƒ€μž… 체컀λ₯Ό ν†΅κ³Όν–ˆμ„ κ²λ‹ˆλ‹€. logProductλŠ” 비ꡬ쑰화 할당문을 μ‚¬μš©ν•΄ κ΅¬ν˜„ν•˜λŠ” 게 λ‚«μŠ΅λ‹ˆλ‹€.
비ꡬ쑰화 할당문은 λͺ¨λ“  지역 λ³€μˆ˜μ˜ νƒ€μž…μ΄ μΆ”λ‘ λ˜λ„λ‘ ν•©λ‹ˆλ‹€. 여기에 μΆ”κ°€λ‘œ λͺ…μ‹œμ  νƒ€μž… ꡬ문을 λ„£λŠ”λ‹€λ©΄ λΆˆν•„μš”ν•œ νƒ€μž… μ„ μ–ΈμœΌλ‘œ 인해 μ½”λ“œκ°€ λ²ˆμž‘ν•΄μ§‘λ‹ˆλ‹€.

function logProduct(product: Product) {
const { id, name, price }: { id: string; name: string; price: number; } = product;
console.log(id, name, price);
}

ν•¨μˆ˜ λ§€κ°œλ³€μˆ˜μ— νƒ€μž… ꡬ문을 μƒλž΅ν•˜λŠ” κ²½μš°λ„ κ°„ν˜Ή μžˆμŠ΅λ‹ˆλ‹€. 기본값이 μžˆλŠ” 경우λ₯Ό 예둜 λ“€μ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

function parseNumber(str: string, base = 10) {
// ...
}

μ—¬κΈ°μ„œ 기본값이 10이기 λ•Œλ¬Έμ— base의 νƒ€μž…μ€ number둜 μΆ”λ‘ λ©λ‹ˆλ‹€.
보톡 νƒ€μž… 정보가 μžˆλŠ” λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œ, 콜백 ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ μžλ™μœΌλ‘œ μΆ”λ‘ λ©λ‹ˆλ‹€. λ‹€μŒ μ˜ˆμ œμ—μ„œ express HTTP μ„œλ²„ 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” request와 response의 νƒ€μž… 선언은 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

app.get('/health', (request, response) => {
response.send('OK');
});

νƒ€μž…μ΄ 좔둠될 수 μžˆμŒμ—λ„ μ—¬μ „νžˆ νƒ€μž…μ„ λͺ…μ‹œν•˜κ³  싢은 λͺ‡ 가지 상황이 μžˆμŠ΅λ‹ˆλ‹€. 그쀑 ν•˜λ‚˜λŠ” 객체 λ¦¬ν„°λŸ΄μ„ μ •μ˜ν•  λ•Œμž…λ‹ˆλ‹€.

const elmo: Product = {
name: 'Tickle Me Elmo',
id: '048188 627152',
price: 28.99,
};

이런 μ •μ˜μ— νƒ€μž…μ„ λͺ…μ‹œν•˜λ©΄, μž‰μ—¬ 속성 체크(μ•„μ΄ν…œ 11)κ°€ λ™μž‘ν•©λ‹ˆλ‹€. μž‰μ—¬ 속성 μ²΄ν¬λŠ” 특히 선택적 속성이 μžˆλŠ” νƒ€μž…μ˜ μ˜€νƒ€ 같은 였λ₯˜λ₯Ό μž‘λŠ” 데 νš¨κ³Όμ μž…λ‹ˆλ‹€. 그리고 λ³€μˆ˜κ°€ μ‚¬μš©λ˜λŠ” μˆœκ°„μ΄ μ•„λ‹Œ ν• λ‹Ήν•˜λŠ” μ‹œμ μ— 였λ₯˜κ°€ ν‘œμ‹œλ˜λ„λ‘ ν•΄ μ€λ‹ˆλ‹€.
λ§Œμ•½ νƒ€μž… ꡬ문을 μ œκ±°ν•œλ‹€λ©΄ μž‰μ—¬ 속성 체크가 λ™μž‘ν•˜μ§€ μ•Šκ³ , 객체λ₯Ό μ„ μ–Έν•œ 곳이 μ•„λ‹ˆλΌ 객체가 μ‚¬μš©λ˜λŠ” κ³³μ—μ„œ νƒ€μž… 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

λ§ˆμ°¬κ°€μ§€λ‘œ ν•¨μˆ˜μ˜ λ°˜ν™˜μ—λ„ νƒ€μž…μ„ λͺ…μ‹œν•˜μ—¬ 였λ₯˜λ₯Ό 방지할 수 μžˆμŠ΅λ‹ˆλ‹€. νƒ€μž… 좔둠이 κ°€λŠ₯할지라도 κ΅¬ν˜„μƒμ˜ 였λ₯˜κ°€ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•œ κ³³κΉŒμ§€ 영ν–₯을 λ―ΈμΉ˜μ§€ μ•Šλ„λ‘ ν•˜κΈ° μœ„ν•΄ νƒ€μž… ꡬ문을 λͺ…μ‹œν•˜λŠ” 게 μ’‹μŠ΅λ‹ˆλ‹€.
주식 μ‹œμ„Έλ₯Ό μ‘°νšŒν•˜λŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν–ˆλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

function getQuote(ticker: string) {
return fetch(`https://quotes.example.com/?q=${ticker}`)
.then(response => response.json());
}

이미 μ‘°νšŒν•œ 쒅볡을 λ‹€μ‹œ μš”μ²­ν•˜μ§€ μ•Šλ„λ‘ μΊμ‹œλ₯Ό μΆ”κ°€ν•©λ‹ˆλ‹€.

const cache: { [ticker: string]: number } = {};
function getQuote(ticker: string) {
if (ticker in cache) {
return cache[ticker];
}

return fetch(`https://quotes.example.com/?q=${ticker}`)
.then(response => response.json())
.then(quote => {
cache[ticker] = quote;
return quote;
});
}

μ‹€ν–‰ν•΄ 보면 였λ₯˜λŠ” getQuote λ‚΄λΆ€κ°€ μ•„λ‹Œ getQuoteλ₯Ό ν˜ΈμΆœν•œ μ½”λ“œμ—μ„œ λ°œμƒν•©λ‹ˆλ‹€.
μ΄λ•Œ μ˜λ„λœ λ°˜ν™˜ νƒ€μž…(Promise<number>)을 λͺ…μ‹œν•œλ‹€λ©΄, μ •ν™•ν•œ μœ„μΉ˜μ— 였λ₯˜κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.

const cache: { [ticker: string]: number } = {};
function getQuote(ticker: string): Promise<number> {
if (ticker in cache) {
return cache[ticker]; // μ—λŸ¬
}

// ...
}

λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•˜λ©΄, κ΅¬ν˜„μƒμ˜ 였λ₯˜κ°€ μ‚¬μš©μž μ½”λ“œμ˜ 였λ₯˜λ‘œ ν‘œμ‹œλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
였λ₯˜μ˜ μœ„μΉ˜λ₯Ό μ œλŒ€λ‘œ ν‘œμ‹œν•΄ μ£ΌλŠ” 이점 외에도, λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•΄μ•Ό ν•˜λŠ” μ΄μœ κ°€ 두 가지 더 μžˆμŠ΅λ‹ˆλ‹€.
첫 λ²ˆμ§ΈλŠ” λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•˜λ©΄ ν•¨μˆ˜μ— λŒ€ν•΄ λ”μš± λͺ…ν™•ν•˜κ²Œ μ•Œ 수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€. λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•˜λ €λ©΄ κ΅¬ν˜„ν•˜κΈ° 전에 μž…λ ₯ νƒ€μž…κ³Ό 좜λ ₯ νƒ€μž…μ΄ 무엇인지 μ•Œμ•„μ•Ό ν•©λ‹ˆλ‹€. 좔후에 μ½”λ“œκ°€ 쑰금 λ³€κ²½λ˜μ–΄λ„ κ·Έ ν•¨μˆ˜μ˜ μ‹œκ·Έλ‹ˆμ²˜λŠ” μ‰½κ²Œ 바꾸지 μ•ŠμŠ΅λ‹ˆλ‹€. 미리 νƒ€μž…μ„ λͺ…μ‹œν•˜λŠ” 방법은, ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜κΈ° 전에 ν…ŒμŠ€νŠΈλ₯Ό λ¨Όμ € μž‘μ„±ν•˜λŠ” ν…ŒμŠ€νŠΈ 주도 개발(test driven development, TDD)κ³Ό λΉ„μŠ·ν•©λ‹ˆλ‹€. 전체 νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λ₯Ό λ¨Όμ € μž‘μ„±ν•˜λ©΄ κ΅¬ν˜„μ— λ§žμΆ”μ–΄ μ£Όλ¨Ήκ΅¬κ΅¬μ‹μœΌλ‘œ μ‹œκ·Έλ‹ˆμ²˜κ°€ μž‘μ„±λ˜λŠ” 것을 λ°©μ§€ν•˜κ³  μ œλŒ€λ‘œ μ›ν•˜λŠ” λͺ¨μ–‘을 μ–»κ²Œ λ©λ‹ˆλ‹€.
λ°˜ν™˜κ°’μ˜ νƒ€μž…μ„ λͺ…μ‹œν•΄μ•Ό ν•˜λŠ” 두 번째 μ΄μœ λŠ” λͺ…λͺ…λœ νƒ€μž…μ„ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œμž…λ‹ˆλ‹€. λ°˜ν™˜ νƒ€μž…μ„ λͺ…μ‹œν•˜λ©΄ λ”μš± 직관적인 ν‘œν˜„μ΄ λ©λ‹ˆλ‹€. 그리고 λ°˜ν™˜ 값을 λ³„λ„μ˜ νƒ€μž…μœΌλ‘œ μ •μ˜ν•˜λ©΄ νƒ€μž…μ— λŒ€ν•œ 주석을 μž‘μ„±ν•  수 μžˆμ–΄μ„œ, λ”μš± μžμ„Έν•œ μ„€λͺ…이 κ°€λŠ₯ν•©λ‹ˆλ‹€. μΆ”λ‘ λœ λ°˜ν™˜ νƒ€μž…μ΄ λ³΅μž‘ν•΄μ§ˆμˆ˜λ‘ λͺ…λͺ…λœ νƒ€μž…μ˜ μ œκ³΅ν•˜λŠ” 이점은 μ»€μ§‘λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 20. λ‹€λ₯Έ νƒ€μž…μ—λŠ” λ‹€λ₯Έ λ³€μˆ˜ μ‚¬μš©ν•˜κΈ°β€‹

μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” ν•œ λ³€μˆ˜λ₯Ό λ‹€λ₯Έ λͺ©μ μ„ κ°€μ§€λŠ” λ‹€λ₯Έ νƒ€μž…μœΌλ‘œ μž¬μ‚¬μš©ν•΄λ„ λ©λ‹ˆλ‹€.
반면 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” 두 가지 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

let id = "12-34-56";
fetchProduct(id);

id = 123456;
// ~~ '123456' ν˜•μ‹μ€ 'string' ν˜•μ‹μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.
fetchProductBySerialNumber(id);

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” "12-34-56"μ΄λΌλŠ” 값을 보고, id의 νƒ€μž…μ„ string으둜 μΆ”λ‘ ν–ˆμŠ΅λ‹ˆλ‹€. string νƒ€μž…μ—λŠ” number νƒ€μž…μ„ ν• λ‹Ήν•  수 μ—†κΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.
μ—¬κΈ°μ„œ "λ³€μˆ˜μ˜ 값은 λ°”λ€” 수 μžˆμ§€λ§Œ κ·Έ νƒ€μž…μ€ 보톡 λ°”λ€Œμ§€ μ•ŠλŠ”λ‹€"λŠ” μ€‘μš”ν•œ 관점을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. νƒ€μž…μ„ λ°”κΏ€ 수 μžˆλŠ” ν•œ 가지 방법은 λ²”μœ„λ₯Ό μ’νžˆλŠ” 것인데, μƒˆλ‘œμš΄ λ³€μˆ˜κ°’μ„ ν¬ν•¨ν•˜λ„λ‘ ν™•μž₯ν•˜λŠ” 것이 μ•„λ‹ˆλΌ νƒ€μž…μ„ 더 μž‘κ²Œ μ œν•œν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

id의 νƒ€μž…μ„ 바꾸지 μ•ŠμœΌλ €λ©΄, stringκ³Ό numberλ₯Ό λͺ¨λ‘ 포함할 수 μžˆλ„λ‘ νƒ€μž…μ„ ν™•μž₯ν•˜λ©΄ λ©λ‹ˆλ‹€. string|number둜 ν‘œν˜„ν•˜λ©°, μœ λ‹ˆμ˜¨ νƒ€μž…μ΄λΌκ³  ν•©λ‹ˆλ‹€.

let id: string | number = "12-34-56";
fetchProduct(id);
id = 123456;
fetchProductBySerialNumber(id);

μœ λ‹ˆμ˜¨ νƒ€μž…μœΌλ‘œ μ½”λ“œκ°€ λ™μž‘ν•˜κΈ°λŠ” ν•˜κ² μ§€λ§Œ 더 λ§Žμ€ λ¬Έμ œκ°€ 생길 수 μžˆμŠ΅λ‹ˆλ‹€. idλ₯Ό μ‚¬μš©ν•  λ•Œλ§ˆλ‹€ 값이 μ–΄λ–€ νƒ€μž…μΈμ§€ 확인해야 ν•˜κΈ° λ•Œλ¬Έμ— μœ λ‹ˆμ˜¨ νƒ€μž…μ€ stringμ΄λ‚˜ number 같은 κ°„λ‹¨ν•œ νƒ€μž…μ— λΉ„ν•΄ 닀루기 더 μ–΄λ ΅μŠ΅λ‹ˆλ‹€. 차라리 λ³„λ„μ˜ λ³€μˆ˜λ₯Ό λ„μž…ν•˜λŠ” 것이 λ‚«μŠ΅λ‹ˆλ‹€.

const id = "12-34-56";
fetchProduct(id);

const serial = 123456;
fetchProductBySerialNumber(serial);

μ•žμ˜ μ˜ˆμ œμ—μ„œ 첫 번째 id와 μž¬μ‚¬μš©ν•œ 두 번째 idλŠ” μ„œλ‘œ 관련이 μ—†μ—ˆμŠ΅λ‹ˆλ‹€. κ·Έλƒ₯ λ³€μˆ˜λ₯Ό μž¬μ‚¬μš©ν–ˆμ„ λΏμž…λ‹ˆλ‹€. λ³€μˆ˜λ₯Ό λ¬΄λΆ„λ³„ν•˜κ²Œ μž¬μ‚¬μš©ν•˜λ©΄ νƒ€μž… 체컀와 μ‚¬λžŒ λͺ¨λ‘μ—κ²Œ ν˜Όλž€μ„ 쀄 λΏμž…λ‹ˆλ‹€.
λ‹€λ₯Έ νƒ€μž…μ—λŠ” λ³„λ„μ˜ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” 게 λ°”λžŒμ§ν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ„œλ‘œ 관련이 μ—†λŠ” 두 개의 값을 λΆ„λ¦¬ν•©λ‹ˆλ‹€.
  • λ³€μˆ˜λͺ…을 더 ꡬ체적으둜 지을 수 μžˆμŠ΅λ‹ˆλ‹€.
  • νƒ€μž… 좔둠을 ν–₯μƒμ‹œν‚€λ©°, νƒ€μž… ꡬ문이 λΆˆν•„μš”ν•΄μ§‘λ‹ˆλ‹€.
  • νƒ€μž…μ΄ μ’€ 더 κ°„κ²°ν•΄μ§‘λ‹ˆλ‹€.
  • let λŒ€μ‹  const둜 λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜κ²Œ λ©λ‹ˆλ‹€. const둜 λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λ©΄ μ½”λ“œκ°€ 간결해지고, νƒ€μž… 체컀가 νƒ€μž…μ„ μΆ”λ‘ ν•˜κΈ°μ—λ„ μ’‹μŠ΅λ‹ˆλ‹€.

νƒ€μž…μ΄ λ°”λ€ŒλŠ” λ³€μˆ˜λŠ” λ˜λ„λ‘ ν”Όν•΄μ•Ό ν•˜λ©°, λͺ©μ μ΄ λ‹€λ₯Έ κ³³μ—λŠ” λ³„λ„μ˜ λ³€μˆ˜λͺ…을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 21. νƒ€μž… λ„“νžˆκΈ°β€‹

μƒμˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ λ³€μˆ˜λ₯Ό μ΄ˆκΈ°ν™”ν•  λ•Œ νƒ€μž…μ„ λͺ…μ‹œν•˜μ§€ μ•ŠμœΌλ©΄ νƒ€μž… μ²΄μ»€λŠ” νƒ€μž…μ„ κ²°μ •ν•΄μ•Ό ν•©λ‹ˆλ‹€. 이 말은 μ§€μ •λœ 단일 값을 가지고 ν• λ‹Ή κ°€λŠ₯ν•œ κ°’λ“€μ˜ 집합을 μœ μΆ”ν•΄μ•Ό ν•œλ‹€λŠ” λœ»μž…λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” μ΄λŸ¬ν•œ 과정을 "λ„“νžˆκΈ°"라고 λΆ€λ¦…λ‹ˆλ‹€.

벑터λ₯Ό λ‹€λ£¨λŠ” 라이브러리λ₯Ό μž‘μ„±ν•œλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 3D 벑터에 λŒ€ν•œ νƒ€μž…κ³Ό κ·Έ μš”μ†Œλ“€μ˜ 값을 μ–»λŠ” ν•¨μˆ˜λ₯Ό μž‘μ„±ν•©λ‹ˆλ‹€.

interface Vector3 { x: number; y: number; z: number; }
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
return vector[axis];
}

Vector3 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•œ λ‹€μŒ μ½”λ“œλŠ” λŸ°νƒ€μž„μ— 였λ₯˜ 없이 μ‹€ν–‰λ˜μ§€λ§Œ, νŽΈμ§‘κΈ°μ—μ„œλŠ” 였λ₯˜κ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.

let x = 'x';
let vec = { x: 10, y: 20, z: 30 };
getComponent(vec, x); // ~~ 'string' ν˜•μ‹μ˜ μΈμˆ˜λŠ” "x" | "y" | "z" ν˜•μ‹μ˜ λ§€κ°œλ³€μˆ˜μ— ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

getComponent ν•¨μˆ˜λŠ” 두 번째 λ§€κ°œλ³€μˆ˜μ— "x" | "y" | "z" νƒ€μž…μ„ κΈ°λŒ€ν–ˆμ§€λ§Œ, x의 νƒ€μž…μ€ ν• λ‹Ή μ‹œμ μ— λ„“νžˆκΈ°κ°€ λ™μž‘ν•΄μ„œ string으둜 μΆ”λ‘ λ˜μ—ˆμŠ΅λ‹ˆλ‹€. string νƒ€μž…μ€ "x" | "y" | "z" νƒ€μž…μ— 할당이 λΆˆκ°€λŠ₯ν•˜λ―€λ‘œ 였λ₯˜κ°€ 된 κ²ƒμž…λ‹ˆλ‹€.
νƒ€μž… λ„“νžˆκΈ°κ°€ 진행될 λ•Œ, 주어진 κ°’μœΌλ‘œ μΆ”λ‘  κ°€λŠ₯ν•œ νƒ€μž…μ΄ μ—¬λŸ¬ 개이기 λ•Œλ¬Έμ— 과정이 μƒλ‹Ήνžˆ λͺ¨ν˜Έν•©λ‹ˆλ‹€.
νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μž‘μ„±μžμ˜ μ˜λ„λ₯Ό μΆ”μΈ‘ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 아무리 μ˜λ¦¬ν•˜λ”λΌλ„ μ‚¬λžŒμ˜ λ§ˆμŒκΉŒμ§€ 읽을 μˆ˜λŠ” μ—†κ³  λ”°λΌμ„œ μΆ”μΈ‘ν•œ 닡이 항상 μ˜³μ„ μˆ˜λ„ μ—†μŠ΅λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” x의 νƒ€μž…μ„ string으둜 μΆ”λ‘ ν•  λ•Œ, λͺ…ν™•μ„±κ³Ό μœ μ—°μ„± μ‚¬μ΄μ˜ κ· ν˜•μ„ μœ μ§€ν•˜λ €κ³  ν•©λ‹ˆλ‹€. 일반적인 κ·œμΉ™μ€ λ³€μˆ˜κ°€ μ„ μ–Έλœ ν›„λ‘œλŠ” νƒ€μž…μ΄ λ°”λ€Œμ§€ μ•Šμ•„μ•Ό ν•˜λ―€λ‘œ, string|RegExpλ‚˜ string|string[]μ΄λ‚˜ any보닀 string을 μ‚¬μš©ν•˜λŠ” 게 λ‚«μŠ΅λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λ„“νžˆκΈ°μ˜ 과정을 μ œμ–΄ν•  수 μžˆλ„λ‘ λͺ‡ 가지 방법을 μ œκ³΅ν•©λ‹ˆλ‹€. λ„“νžˆκΈ° 과정을 μ œμ–΄ν•  수 μžˆλŠ” 첫 번째 방법은 constμž…λ‹ˆλ‹€. λ§Œμ•½ let λŒ€μ‹  const둜 λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λ©΄ 더 쒁은 νƒ€μž…μ΄ λ©λ‹ˆλ‹€. μ‹€μ œλ‘œ constλ₯Ό μ‚¬μš©ν•˜λ©΄ μ•žμ—μ„œ λ°œμƒν•œ 였λ₯˜κ°€ ν•΄κ²°λ©λ‹ˆλ‹€.

const x = 'x'; // νƒ€μž…μ΄ "x"
let vec = { x: 10, y: 20, z: 30 };
getComponent(vec, x); // 정상

이제 xλŠ” μž¬ν• λ‹Ήλ  수 μ—†μœΌλ―€λ‘œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μ˜μ‹¬μ˜ 여지 없이 더 쒁은 νƒ€μž…μœΌλ‘œ μΆ”λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 그리고 문자 λ¦¬ν„°λŸ΄ νƒ€μž… xλŠ” x|y|z에 ν• λ‹Ή κ°€λŠ₯ν•˜λ―€λ‘œ μ½”λ“œκ°€ νƒ€μž… 체컀λ₯Ό ν†΅κ³Όν•©λ‹ˆλ‹€.
κ·ΈλŸ¬λ‚˜ constλŠ” 만λŠ₯이 μ•„λ‹™λ‹ˆλ‹€. 객체와 λ°°μ—΄μ˜ κ²½μš°μ—λŠ” μ—¬μ „νžˆ λ¬Έμ œκ°€ μžˆμŠ΅λ‹ˆλ‹€. νŠœν”Œ νƒ€μž…μ„ μΆ”λ‘ ν•΄μ•Ό 할지, μš”μ†Œλ“€μ„ μ–΄λ–€ νƒ€μž…μœΌλ‘œ μΆ”λ‘ ν•΄μ•Ό 할지 μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€. λΉ„μŠ·ν•œ λ¬Έμ œκ°€ κ°μ²΄μ—μ„œλ„ λ°œμƒν•©λ‹ˆλ‹€.

μ•žμ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” λͺ…ν™•μ„±κ³Ό μœ μ—°μ„± μ‚¬μ΄μ˜ κ· ν˜•μ„ μœ μ§€ν•˜λ €κ³  ν•©λ‹ˆλ‹€. 였λ₯˜λ₯Ό 작기 μœ„ν•΄μ„œλŠ” μΆ©λΆ„νžˆ ꡬ체적으둜 νƒ€μž…μ„ μΆ”λ‘ ν•΄μ•Ό ν•˜μ§€λ§Œ, 잘λͺ»λœ 좔둠을 ν•  μ •λ„λ‘œ ꡬ체적으둜 μˆ˜ν–‰ν•˜μ§€λŠ” μ•ŠμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, 1κ³Ό 같은 κ°’μœΌλ‘œ μ΄ˆκΈ°ν™”λ˜λŠ” 속성을 μ λ‹Ήνžˆ number의 νƒ€μž…μœΌλ‘œ μΆ”λ‘ ν•©λ‹ˆλ‹€.
νƒ€μž…μ˜ μΆ”λ‘ μ˜ 강도λ₯Ό 직접 μ œμ–΄ν•˜λ €λ©΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ κΈ°λ³Έ λ™μž‘μ„ μž¬μ •μ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ κΈ°λ³Έ λ™μž‘μ„ μž¬μ •μ˜ν•˜λŠ” μ„Έ 가지 방법이 μžˆμŠ΅λ‹ˆλ‹€.

첫 번째, λͺ…μ‹œμ  νƒ€μž… ꡬ문을 μ œκ³΅ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

const v: { x: 1|3|5 } = {
x: 1,
}; // νƒ€μž…μ΄ { x: 1|3|5; }

두 번째, νƒ€μž… 체컀에 좔가적인 λ¬Έλ§₯을 μ œκ³΅ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. (예λ₯Ό λ“€μ–΄, ν•¨μˆ˜μ˜ λ§€κ°œλ³€μˆ˜λ‘œ 값을 전달) μ•„μ΄ν…œ 26은 νƒ€μž… μΆ”λ‘  κ³Όμ •μ—μ„œ λ¬Έλ§₯의 역할에 λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ„ λ‹€λ£Ήλ‹ˆλ‹€.
μ„Έ 번째, const 단언문을 μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. const 단언문과 λ³€μˆ˜ 선언에 μ“°μ΄λŠ” letμ΄λ‚˜ const와 ν˜Όλ™ν•΄μ„œλŠ” μ•ˆ λ©λ‹ˆλ‹€. const 단언문은 μ˜¨μ „νžˆ νƒ€μž… κ³΅κ°„μ˜ κΈ°λ²•μž…λ‹ˆλ‹€. λ‹€μŒ 예제λ₯Ό 톡해 각 λ³€μˆ˜μ— μΆ”λ‘ λœ νƒ€μž…μ˜ 차이점을 μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

const v1 = {
x: 1,
y: 2,
}; // νƒ€μž…μ€ { x: number; y: number; }
const v2 = {
x: 1 as const,
y: 2,
}; // νƒ€μž…μ€ { x: 1; y: number; }
const v3 = {
x: 1,
y: 2,
} as const; // νƒ€μž…μ€ { readonly x: 1; readonly y: 2; }

κ°’ 뒀에 as constλ₯Ό μž‘μ„±ν•˜λ©΄, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μ΅œλŒ€ν•œ 쒁은 νƒ€μž…μœΌλ‘œ μΆ”λ‘ ν•©λ‹ˆλ‹€. v3μ—λŠ” λ„“νžˆκΈ°κ°€ λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. v3이 μ§„μ§œ μƒμˆ˜λΌλ©΄, 주석에 λ³΄μ΄λŠ” μΆ”λ‘ λœ νƒ€μž…μ΄ μ‹€μ œλ‘œ μ›ν•˜λŠ” ν˜•νƒœμΌ κ²ƒμž…λ‹ˆλ‹€. λ˜ν•œ 배열을 νŠœν”Œ νƒ€μž…μœΌλ‘œ μΆ”λ‘ ν•  λ•Œλ„ as constλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const a1 = [1, 2, 3]; // νƒ€μž…μ΄ number[]
const a2 = [1, 2, 3] as const; // νƒ€μž…μ΄ readonly [1, 2, 3]

λ„“νžˆκΈ°λ‘œ 인해 였λ₯˜κ°€ λ°œμƒν•œλ‹€κ³  μƒκ°λ˜λ©΄, λͺ…μ‹œμ  νƒ€μž… ꡬ문 λ˜λŠ” const 단언문을 μΆ”κ°€ν•˜λŠ” 것을 κ³ λ €ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ‹¨μ–Έλ¬ΈμœΌλ‘œ 인해 좔둠이 μ–΄λ–»κ²Œ λ³€ν™”ν•˜λŠ”μ§€ νŽΈμ§‘κΈ°μ—μ„œ 주기적으둜 νƒ€μž…μ„ μ‚΄νŽ΄λ³΄κΈ° λ°”λžλ‹ˆλ‹€.(μ•„μ΄ν…œ 6)

πŸ₯• μ•„μ΄ν…œ 22. νƒ€μž… μ’νžˆκΈ°β€‹

νƒ€μž… μ’νžˆκΈ°λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ 넓은 νƒ€μž…μœΌλ‘œλΆ€ν„° 쒁은 νƒ€μž…μœΌλ‘œ μ§„ν–‰ν•˜λŠ” 과정을 λ§ν•©λ‹ˆλ‹€. μ•„λ§ˆλ„ κ°€μž₯ 일반적인 μ˜ˆμ‹œλŠ” null 체크일 κ²λ‹ˆλ‹€.

const el = document.getElementById('foo'); // νƒ€μž…μ΄ HTMLElement | null
if (el) {
el // νƒ€μž…μ΄ HTMLElement
el.innerHTML = 'Party Time'.blink();
} else {
el // νƒ€μž…μ΄ null
alert('No element #foo');
}

νƒ€μž… μ²΄μ»€λŠ” 일반적으둜 μ΄λŸ¬ν•œ μ‘°κ±΄λ¬Έμ—μ„œ νƒ€μž… 쒁히기λ₯Ό 잘 ν•΄λ‚΄μ§€λ§Œ, νƒ€μž… 별칭이 μ‘΄μž¬ν•œλ‹€λ©΄ κ·ΈλŸ¬μ§€ λͺ»ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. νƒ€μž… 별칭에 λŒ€ν•œ λ‚΄μš©μ€ μ•„μ΄ν…œ 24μ—μ„œ λ‹€λ£¨κ² μŠ΅λ‹ˆλ‹€.

λΆ„κΈ°λ¬Έμ—μ„œ μ˜ˆμ™Έλ₯Ό λ˜μ§€κ±°λ‚˜ ν•¨μˆ˜λ₯Ό λ°˜ν™˜ν•˜μ—¬ λΈ”λ‘μ˜ λ‚˜λ¨Έμ§€ λΆ€λΆ„μ—μ„œ λ³€μˆ˜μ˜ νƒ€μž…μ„ 쒁힐 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

const el = document.getElementById('foo'); // νƒ€μž…μ΄ HTMLElement | null
if (!el) throw new Error('Unable to find #foo');
el; // 이제 νƒ€μž…μ€ HTMLElement
el.innerHTML = 'Party Time'.blink();

이 외에도 νƒ€μž…μ„ μ’νžˆλŠ” 방법은 많이 μžˆμŠ΅λ‹ˆλ‹€. λ‹€μŒμ€ instanceofλ₯Ό μ‚¬μš©ν•΄μ„œ νƒ€μž…μ„ μ’νžˆλŠ” μ˜ˆμ œμž…λ‹ˆλ‹€.

function contains(text: string, search: string | RegExp) {
if (search instanceof RegExp) {
search // νƒ€μž…μ΄ RegExp
return !!search.exec(text);
}

search // νƒ€μž…μ΄ string
return text.includes(search);
}

속성 μ²΄ν¬λ‘œλ„ νƒ€μž…μ„ 쒁힐 수 μžˆμŠ΅λ‹ˆλ‹€.

interface A { a: number }
interface B { b: number }
function pickAB(ab: A | B) {
if ('a' in ab) {
ab // νƒ€μž…μ΄ A
} else {
ab // νƒ€μž…μ΄ B
}
ab // νƒ€μž…μ΄ A | B
}

Array.isArray 같은 일뢀 λ‚΄μž₯ ν•¨μˆ˜λ‘œλ„ νƒ€μž…μ„ 쒁힐 수 μžˆμŠ΅λ‹ˆλ‹€.

function contains(text: string, terms: string|string[]) {
const termList = Array.isArray(terms) ? terms : [terms];
termList // νƒ€μž…μ΄ string[]
// ...
}

νƒ€μž…μ„ μ„£λΆˆλ¦¬ νŒλ‹¨ν•˜λŠ” μ‹€μˆ˜λ₯Ό 저지λ₯΄κΈ° μ‰¬μš°λ―€λ‘œ λ‹€μ‹œ ν•œλ²ˆ 꼼꼼히 λ”°μ Έ 봐야 ν•©λ‹ˆλ‹€.

function foo(x?: number|string|null) {
if (!x) {
x; // νƒ€μž…μ΄ string | number | null | undefined
}
}

빈 λ¬Έμžμ—΄ ''와 0 λͺ¨λ‘ falseκ°€ 되기 λ•Œλ¬Έμ—, νƒ€μž…μ€ μ „ν˜€ μ’ν˜€μ§€μ§€ μ•Šμ•˜κ³  xλŠ” μ—¬μ „νžˆ 블둝 λ‚΄μ—μ„œ string λ˜λŠ” numberκ°€ λ©λ‹ˆλ‹€.

νƒ€μž…μ„ μ’νžˆλŠ” 또 λ‹€λ₯Έ 일반적인 방법은 λͺ…μ‹œμ  'νƒœκ·Έ'λ₯Ό λΆ™μ΄λŠ” κ²ƒμž…λ‹ˆλ‹€.

interface UploadEvent { type: 'upload'; filename: string; contents: string }
interface DownloadEvent { type: 'download'; filename: string; }
type AppEvent = UploadEvent | DownloadEvent;
function handleEvent(e: AppEvent) {
switch (e.type) {
case 'download':
e // νƒ€μž…μ΄ DownloadEvent
break;
case 'upload':
e; // νƒ€μž…μ΄ UploadEvent
break;
}
}

이 νŒ¨ν„΄μ€ 'νƒœκ·Έλœ μœ λ‹ˆμ˜¨' λ˜λŠ” 'κ΅¬λ³„λœ μœ λ‹ˆμ˜¨'이라고 뢈리며, νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ–΄λ””μ—μ„œλ‚˜ μ°Ύμ•„λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.
λ§Œμ•½ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ νƒ€μž…μ„ μ‹λ³„ν•˜μ§€ λͺ»ν•œλ‹€λ©΄, 식별을 돕기 μœ„ν•΄ μ»€μŠ€ν…€ ν•¨μˆ˜λ₯Ό λ„μž…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

function isInputElement(el: HTMLElement): el is HTMLInputElement {
return 'value' in el;
}

function getElementContent(el: HTMLElement) {
if (isInputElement(el)) {
el; // νƒ€μž…μ΄ HTMLInputElement
return el.value;
}
el; // νƒ€μž…μ΄ HTMLElement
return el.textContent;
}

μ΄λŸ¬ν•œ 기법을 'μ‚¬μš©μž μ •μ˜ νƒ€μž… κ°€λ“œ'라고 ν•©λ‹ˆλ‹€. λ°˜ν™˜ νƒ€μž…μ˜ el is HTMLInputElementλŠ” ν•¨μˆ˜μ˜ λ°˜ν™˜μ΄ true인 경우, νƒ€μž… μ²΄μ»€μ—κ²Œ λ§€κ°œλ³€μˆ˜μ˜ νƒ€μž…μ„ 쒁힐 수 μžˆλ‹€κ³  μ•Œλ € μ€λ‹ˆλ‹€.
μ–΄λ–€ ν•¨μˆ˜λ“€μ€ νƒ€μž… κ°€λ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ°°μ—΄κ³Ό 객체의 νƒ€μž… 쒁히기λ₯Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ°°μ—΄μ—μ„œ μ–΄λ–€ 탐색을 μˆ˜ν–‰ν•  λ•Œ undefinedκ°€ 될 수 μžˆλŠ” νƒ€μž…μ„ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const jackson5 = ['Jackie', 'Tito', 'Jermaine', 'Marlon', 'Michael'];
const members = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who)
); // νƒ€μž…μ΄ (string | undefined)[]

filter ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄ undefinedλ₯Ό 걸러 λ‚΄λ €κ³  해도 잘 λ™μž‘ν•˜μ§€ μ•Šμ„ κ²λ‹ˆλ‹€.

const members = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who)
).filter(who => who !== undefined); // νƒ€μž…μ΄ (string | undefined)[]

이럴 λ•Œ νƒ€μž… κ°€λ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ νƒ€μž…μ„ 쒁힐 수 μžˆμŠ΅λ‹ˆλ‹€.

function isDefined<T>(x: T | undefined): x is T {
return x !== undefined;
}
const members = ['Janet', 'Michael'].map(
who => jackson5.find(n => n === who)
).filter(isDefined); // νƒ€μž…μ΄ string[]

πŸ₯• μ•„μ΄ν…œ 23. ν•œκΊΌλ²ˆμ— 객체 μƒμ„±ν•˜κΈ°β€‹

객체λ₯Ό 생성할 λ•ŒλŠ” 속성을 ν•˜λ‚˜μ”© μΆ”κ°€ν•˜κΈ°λ³΄λ‹€λŠ” μ—¬λŸ¬ 속성을 ν¬ν•¨ν•΄μ„œ ν•œκΊΌλ²ˆμ— 생성해야 νƒ€μž… 좔둠에 μœ λ¦¬ν•©λ‹ˆλ‹€.

interface Point { x: number; y: number; }

const pt: Point = {
x: 3,
y: 4,
}

μž‘μ€ 객체듀을 μ‘°ν•©ν•΄μ„œ 큰 객체λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•˜λŠ” κ²½μš°μ—λ„ μ—¬λŸ¬ 단계λ₯Ό κ±°μΉ˜λŠ” 것은 쒋지 μ•Šμ€ μƒκ°μž…λ‹ˆλ‹€.
λ‹€μŒκ³Ό 같이 객체 μ „κ°œ μ—°μ‚°μž ...λ₯Ό μ‚¬μš©ν•˜λ©΄ 큰 객체λ₯Ό ν•œκΊΌλ²ˆμ— λ§Œλ“€μ–΄ λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

const pt = { x: 3, y: 4 };
const id = { name: 'Pythagoras' };
const namedPoint = { ...pt, ...id };
namedPoint.name; // 정상.

객체 μ „κ°œ μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜λ©΄ νƒ€μž… κ±±μ • 없이 ν•„λ“œ λ‹¨μœ„λ‘œ 객체λ₯Ό 생성할 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ λͺ¨λ“  μ—…λ°μ΄νŠΈλ§ˆλ‹€ μƒˆ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ 각각 μƒˆλ‘œμš΄ νƒ€μž…μ„ 얻도둝 ν•˜λŠ” 게 μ€‘μš”ν•©λ‹ˆλ‹€.

const pt0 = {};
const pt1 = { ...pt0, x: 3 };
const pt: Point = { ...pt1, y: 4 }; // 정상

νƒ€μž…μ— μ•ˆμ „ν•œ λ°©μ‹μœΌλ‘œ 쑰건뢀 속성을 μΆ”κ°€ν•˜λ €λ©΄, 속성을 μΆ”κ°€ν•˜μ§€ μ•ŠλŠ” null λ˜λŠ” {}으둜 객체 μ „κ°œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

declare let hasMiddle: boolean;
const firstLast = { first: 'Harry', last: 'Truman' };
const president = { ...firstLast, ...(hasMiddle ? { middle: 'S' } : {})};

νŽΈμ§‘κΈ°μ—μ„œ president μ‹¬λ²Œμ— 마우슀λ₯Ό 올렀 보면, νƒ€μž…μ΄ 선택적 속성을 가진 κ²ƒμœΌλ‘œ μΆ”λ‘ λœλ‹€λŠ” 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.

const president: {
middle?: string;
first: string;
last: string;
}

μ „κ°œ μ—°μ‚°μžλ‘œ ν•œκΊΌλ²ˆμ— μ—¬λŸ¬ 속성을 μΆ”κ°€ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

declare let hasDates: boolean;
const nameTitle = { name: 'Khufu', title: 'Pharaoh' };
const pharaoh = {
...nameTitle,
...(hasDates ? { start: -2589, end: -2566 } : {})
};

νŽΈμ§‘κΈ°μ—μ„œ pharaoh μ‹¬λ²Œμ— 마우슀λ₯Ό 올렀 보면, μ΄μ œλŠ” νƒ€μž…μ΄ μœ λ‹ˆμ˜¨μœΌλ‘œ μΆ”λ‘ λ©λ‹ˆλ‹€.

const pharaoh: {
start: number;
end: number;
name: string;
title: string;
} | {
name: string;
title: string;
}

start와 endκ°€ 선택적 ν•„λ“œμ΄κΈ°λ₯Ό μ›ν–ˆλ‹€λ©΄ 이런 κ²°κ³Όκ°€ λ‹Ήν™©μŠ€λŸ¬μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€. 이 νƒ€μž…μ—μ„œλŠ” startλ₯Ό 읽을 수 μ—†μŠ΅λ‹ˆλ‹€.
이 κ²½μš°μ—λŠ” start와 endκ°€ 항상 ν•¨κ»˜ μ •μ˜λ©λ‹ˆλ‹€. 이 점을 κ³ λ €ν•˜λ©΄ μœ λ‹ˆμ˜¨μ„ μ‚¬μš©ν•˜λŠ” 게 κ°€λŠ₯ν•œ κ°’μ˜ 집합을 더 μ •ν™•νžˆ ν‘œν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€. (μ•„μ΄ν…œ 32) 그런데 μœ λ‹ˆμ˜¨λ³΄λ‹€λŠ” 선택적 ν•„λ“œκ°€ λ‹€λ£¨κΈ°μ—λŠ” 더 μ‰¬μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€. 선택적 ν•„λ“œ λ°©μ‹μœΌλ‘œ ν‘œν˜„ν•˜λ €λ©΄ λ‹€μŒμ²˜λŸΌ 헬퍼 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

function addOptional<T extends object, U extends object>(
a: T, b: U | null
): T & Partial<U> {
return { ...a, ...b };
}

const pharaoh = addOptional(
nameTitle,
hasDates ? { start: -2589, end: -2566 } : null
);
pharaoh.start // 정상, νƒ€μž…μ΄ number | undefined

가끔 κ°μ²΄λ‚˜ 배열을 λ³€ν™˜ν•΄μ„œ μƒˆλ‘œμš΄ κ°μ²΄λ‚˜ 배열을 μƒμ„±ν•˜κ³  싢을 수 μžˆμŠ΅λ‹ˆλ‹€. 이런 경우 루프 λŒ€μ‹  λ‚΄μž₯된 ν•¨μˆ˜ν˜• 기법 λ˜λŠ” λ‘œλŒ€μ‹œ 같은 μœ ν‹Έλ¦¬ν‹° 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 'ν•œκΊΌλ²ˆμ— 객체 μƒμ„±ν•˜κΈ°' κ΄€μ μ—μ„œ 보면 μ˜³μŠ΅λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 24. 일관성 μžˆλŠ” 별칭 μ‚¬μš©ν•˜κΈ°β€‹

별칭을 λ‚¨λ°œν•΄μ„œ μ‚¬μš©ν•˜λ©΄ μ œμ–΄ 흐름을 λΆ„μ„ν•˜κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€. λͺ¨λ“  μ–Έμ–΄μ˜ 컴파일러 κ°œλ°œμžλ“€μ€ λ¬΄λΆ„λ³„ν•œ 별칭 μ‚¬μš©μœΌλ‘œ 골치λ₯Ό 썩이고 μžˆμŠ΅λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλ„ λ§ˆμ°¬κ°€μ§€λ‘œ 별칭을 μ‹ μ€‘ν•˜κ²Œ μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έλž˜μ•Ό μ½”λ“œλ₯Ό 잘 이해할 수 있고, 였λ₯˜λ„ μ‰½κ²Œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€κ°ν˜•μ„ ν‘œν˜„ν•˜λŠ” 자료ꡬ쑰λ₯Ό κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface Coordinate {
x: number;
y: number;
}

interface BoundingBox {
x: [number, number];
y: [number, number];
}

interface Polygon {
exterior: Coordinate[];
holes: Coordinate[][];
bbox?: BoundingBox;
}

bbox 속성을 μ‚¬μš©ν•˜λ©΄ μ–΄λ–€ 점이 λ‹€κ°ν˜•μ— ν¬ν•¨λ˜λŠ”μ§€ λΉ λ₯΄κ²Œ 체크할 수 μžˆμŠ΅λ‹ˆλ‹€.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
if (polygon.bbox) {
if (pt.x < polygon.bbox.x[0] || pt.x > polygon.bbox.x[1] || pt.y < polygon.bbox.y[0] || pt.y > polygon.bbox.y[1]) {
return false;
}
}

// ...
}

이 μ½”λ“œλŠ” 잘 μž‘λ™ν•˜μ§€λ§Œ λ°˜λ³΅λ˜λŠ” 뢀뢄이 μ‘΄μž¬ν•©λ‹ˆλ‹€. 특히 polygon.bboxλŠ” 3쀄에 걸쳐 5λ²ˆμ΄λ‚˜ λ“±μž₯ν•©λ‹ˆλ‹€. λ‹€μŒ μ½”λ“œλŠ” 쀑볡을 쀄이기 μœ„ν•΄ μž„μ‹œ λ³€μˆ˜λ₯Ό 뽑아낸 λͺ¨μŠ΅μž…λ‹ˆλ‹€.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (polygon.bbox) {
if (pt.x < box.x[0] || pt.x > box.x[1] || pt.y < box.y[0] || pt.y > box.y[1]) {
// error: 객체가 'undefined'일 수 μžˆμŠ΅λ‹ˆλ‹€.
return false;
}
}

// ...
}

(strictNullChecksλ₯Ό ν™œμ„±ν™”ν–ˆλ‹€κ³  κ°€μ •ν–ˆμŠ΅λ‹ˆλ‹€.) 이 μ½”λ“œλŠ” λ™μž‘ν•˜μ§€λ§Œ νŽΈμ§‘κΈ°μ—μ„œ 였λ₯˜λ‘œ ν‘œμ‹œλ©λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” polygon.bboxλ₯Ό λ³„λ„μ˜ boxλΌλŠ” 별칭을 λ§Œλ“€μ—ˆκ³ , 첫 번째 μ˜ˆμ‹œμ—μ„œλŠ” 잘 λ™μž‘ν–ˆλ˜ μ œμ–΄ 흐름 뢄석을 λ°©ν•΄ν–ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.
속성 μ²΄ν¬λŠ” polygon.bbox의 νƒ€μž…μ„ μ •μ œν–ˆμ§€λ§Œ boxλŠ” 그렇지 μ•Šμ•˜κΈ° λ•Œλ¬Έμ— 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 였λ₯˜λŠ” "별칭은 일관성 있게 μ‚¬μš©ν•œλ‹€"λŠ” κΈ°λ³Έ 원칙을 지킀면 방지할 수 μžˆμŠ΅λ‹ˆλ‹€.

속성 체크에 boxλ₯Ό μ‚¬μš©ν•˜λ„λ‘ μ½”λ“œλ₯Ό λ°”κΏ” λ³΄κ² μŠ΅λ‹ˆλ‹€.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox;
if (box) {
if (pt.x < box.x[0] || pt.x > box.x[1] || pt.y < box.y[0] || pt.y > box.y[1]) {
return false;
}
}

// ...
}

νƒ€μž… 체컀의 λ¬Έμ œλŠ” ν•΄κ²°λ˜μ—ˆμ§€λ§Œ μ½”λ“œλ₯Ό μ½λŠ” μ‚¬λžŒμ—κ²ŒλŠ” λ¬Έμ œκ°€ 남아 μžˆμŠ΅λ‹ˆλ‹€. box와 bboxλŠ” 같은 값인데 λ‹€λ₯Έ 이름을 μ‚¬μš©ν•œ κ²ƒμž…λ‹ˆλ‹€. 객체 비ꡬ쑰화λ₯Ό μ΄μš©ν•˜λ©΄ 보닀 κ°„κ²°ν•œ λ¬Έλ²•μœΌλ‘œ μΌκ΄€λœ 이름을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ°°μ—΄κ³Ό μ€‘μ²©λœ κ΅¬μ‘°μ—μ„œλ„ μ—­μ‹œ μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const { bbox } = polygon;
if (bbox) {
const { x, y } = bbox;
if (pt.x < x[0] || pt.x > x[1] || pt.y < y[0] || pt.y > y[1]) {
return false;
}
}

// ...
}

κ·ΈλŸ¬λ‚˜ 객체 비ꡬ쑰화λ₯Ό μ΄μš©ν•  λ•ŒλŠ” 두 가지λ₯Ό μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • 전체 bbox 속성이 μ•„λ‹ˆλΌ x와 yκ°€ 선택적 속성인 κ²½μš°μ— 속성 체크가 더 ν•„μš”ν•©λ‹ˆλ‹€. λ”°λΌμ„œ νƒ€μž…μ˜ 경계에 null 값을 μΆ”κ°€ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.
  • bboxμ—λŠ” 선택적 속성이 μ ν•©ν–ˆμ§€λ§Œ holesλŠ” 그렇지 μ•ŠμŠ΅λ‹ˆλ‹€. holesκ°€ 선택적이라면, 값이 μ—†κ±°λ‚˜ 빈 λ°°μ—΄μ΄μ—ˆμ„ κ²λ‹ˆλ‹€. 차이가 μ—†λŠ”λ° 이름을 κ΅¬λ³„ν•œ κ²ƒμž…λ‹ˆλ‹€. 빈 배열은 'holes μ—†μŒ'을 λ‚˜νƒ€λ‚΄λŠ” 쒋은 λ°©λ²•μž…λ‹ˆλ‹€.

별칭은 νƒ€μž… 체컀뿐만 μ•„λ‹ˆλΌ λŸ°νƒ€μž„μ—λ„ ν˜Όλ™μ„ μ•ΌκΈ°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const { bbox } = polygon;
if (!bbox) {
calculatePolygonBbox(polygon); // polygon.bboxκ°€ μ±„μ›Œμ§‘λ‹ˆλ‹€.
// 이제 polygon.bbox와 bboxλŠ” λ‹€λ₯Έ 값을 μ°Έμ‘°ν•©λ‹ˆλ‹€!
}

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ μ œμ–΄ 흐름 뢄석은 지역 λ³€μˆ˜μ—λŠ” κ½€ 잘 λ™μž‘ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 객체 μ†μ„±μ—λŠ” μ£Όμ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€.

function fn(p: Polygon) { /* ... */ }

polygon.bbox // νƒ€μž…μ΄ BoundingBox | undefined
if (polygon.bbox) {
polygon.bbox // νƒ€μž…μ΄ BoundingBox
fn(polygon);
polygon.bbox // νƒ€μž…μ΄ BoundingBox
}

fn(polygon) ν˜ΈμΆœμ€ polygon.bboxλ₯Ό μ œκ±°ν•  κ°€λŠ₯성이 μžˆμœΌλ―€λ‘œ νƒ€μž…μ„ BoundingBox | undefined둜 λ˜λŒλ¦¬λŠ” 것이 μ•ˆμ „ν•  κ²ƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ 속성 체크λ₯Ό λ°˜λ³΅ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 쒋지 μ•ŠμŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•¨μˆ˜κ°€ νƒ€μž… μ •μ œλ₯Ό λ¬΄νš¨ν™”ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ‹€μ œλ‘œλŠ” λ¬΄νš¨ν™”λ  κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€. polygon.bbox둜 μ‚¬μš©ν•˜λŠ” λŒ€μ‹  bbox 지역 λ³€μˆ˜λ‘œ λ½‘μ•„λ‚΄μ„œ μ‚¬μš©ν•˜λ©΄ bbox의 νƒ€μž…μ€ μ •ν™•νžˆ μœ μ§€λ˜μ§€λ§Œ, polygon.bbox의 κ°’κ³Ό κ°™κ²Œ μœ μ§€λ˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 25. 비동기 μ½”λ“œμ—λŠ” 콜백 λŒ€μ‹  async ν•¨μˆ˜ μ‚¬μš©ν•˜κΈ°β€‹

콜백이 μ€‘μ²©λœ μ½”λ“œλŠ” μ§κ΄€μ μœΌλ‘œ μ΄ν•΄ν•˜κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€. μš”μ²­λ“€μ„ λ³‘λ ¬λ‘œ μ‹€ν–‰ν•˜κ±°λ‚˜ 였λ₯˜ 상황을 λΉ μ Έλ‚˜μ˜€κ³  μ‹Άλ‹€λ©΄ λ”μš± ν˜Όλž€μŠ€λŸ¬μ›Œμ§‘λ‹ˆλ‹€.
ES2017μ—μ„œλŠ” async와 await ν‚€μ›Œλ“œλ₯Ό λ„μž…ν•˜μ—¬ 콜백 지μ˜₯을 λ”μš± κ°„λ‹¨ν•˜κ²Œ μ²˜λ¦¬ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

async function fetchPages() {
const response1 = await fetch(url1);
const response2 = await fetch(url2);
const response3 = await fetch(url3);
// ...
}

await ν‚€μ›Œλ“œλŠ” 각각의 ν”„λ‘œλ―ΈμŠ€κ°€ 처리될 λ•ŒκΉŒμ§€ fetchPages ν•¨μˆ˜μ˜ 싀행을 멈μΆ₯λ‹ˆλ‹€. async ν•¨μˆ˜ λ‚΄μ—μ„œ await 쀑인 ν”„λ‘œλ―ΈμŠ€κ°€ 거절되면 μ˜ˆμ™Έλ₯Ό λ˜μ§‘λ‹ˆλ‹€. 이λ₯Ό 톡해 일반적인 try/catch ꡬ문을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

async function fetchPages() {
try {
const response1 = await fetch(url1);
const response2 = await fetch(url2);
const response3 = await fetch(url3);
// ...
} catch (e) {
// ...
}
}

μ½œλ°±λ³΄λ‹€λŠ” ν”„λ‘œλ―ΈμŠ€λ‚˜ async/awaitλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ½œλ°±λ³΄λ‹€λŠ” ν”„λ‘œλ―ΈμŠ€κ°€ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κΈ° μ‰½μŠ΅λ‹ˆλ‹€.
  • μ½œλ°±λ³΄λ‹€λŠ” ν”„λ‘œλ―ΈμŠ€κ°€ νƒ€μž…μ„ μΆ”λ‘ ν•˜κΈ° μ‰½μŠ΅λ‹ˆλ‹€.

νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” μ„Έ 가지 response λ³€μˆ˜ 각각의 νƒ€μž…μ„ Response둜 μΆ”λ‘ ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 콜백 μŠ€νƒ€μΌλ‘œ λ™μΌν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λ €λ©΄ 더 λ§Žμ€ μ½”λ“œμ™€ νƒ€μž… ꡬ문이 ν•„μš”ν•©λ‹ˆλ‹€.
ν•œνŽΈ μž…λ ₯된 ν”„λ‘œλ―ΈμŠ€λ“€ 쀑 첫 λ²ˆμ§Έκ°€ 처리될 λ•Œ μ™„λ£Œλ˜λŠ” Promise.race 도 νƒ€μž… μΆ”λ‘ κ³Ό 잘 λ§žμŠ΅λ‹ˆλ‹€. Promise.raceλ₯Ό μ‚¬μš©ν•˜μ—¬ ν”„λ‘œλ―ΈμŠ€μ— νƒ€μž„μ•„μ›ƒμ„ μΆ”κ°€ν•˜λŠ” 방법은 ν”ν•˜κ²Œ μ‚¬μš©λ˜λŠ” νŒ¨ν„΄μž…λ‹ˆλ‹€.

function timeout(millis: number): Promise<never> {
return new Promise((resolve, reject) => {
setTimeout(() => reject('timeout'), millis);
});
}

async function fetchWithTimeout(url: string, ms: number) {
return Promise.race([fetch(url), timeout(ms)]);
}

νƒ€μž… ꡬ문이 없어도 fetchWithTimeout의 λ°˜ν™˜ νƒ€μž…μ€ Promise<Response>둜 μΆ”λ‘ λ©λ‹ˆλ‹€. Promise.race의 λ°˜ν™˜ νƒ€μž…μ€ μž…λ ₯ νƒ€μž…λ“€μ˜ μœ λ‹ˆμ˜¨μ΄κ³ , 이번 κ²½μš°λŠ” Promise<Response | never>κ°€ λ©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ neverμ™€μ˜ μœ λ‹ˆμ˜¨μ€ μ•„λ¬΄λŸ° νš¨κ³Όκ°€ μ—†μœΌλ―€λ‘œ, κ²°κ³Όκ°€ Promise<Response>둜 κ°„λ‹¨ν•΄μ§‘λ‹ˆλ‹€. ν”„λ‘œλ―ΈμŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ νƒ€μž…μŠ€ν¬λ¦½νŠΈμ˜ λͺ¨λ“  νƒ€μž… 좔둠이 μ œλŒ€λ‘œ λ™μž‘ν•©λ‹ˆλ‹€.

가끔 ν”„λ‘œλ―ΈμŠ€λ₯Ό 직접 생성해야 ν•  λ•Œ, 특히 setTimeoutκ³Ό 같은 콜백 APIλ₯Ό λž˜ν•‘ν•  κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ„ νƒμ˜ 여지가 μžˆλ‹€λ©΄ μΌλ°˜μ μœΌλ‘œλŠ” ν”„λ‘œλ―ΈμŠ€λ₯Ό μƒμ„±ν•˜κΈ°λ³΄λ‹€ async/awaitλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” λ‹€μŒ 두 κ°€μ§€μž…λ‹ˆλ‹€.

  • 일반적으둜 더 κ°„κ²°ν•˜κ³  직관적인 μ½”λ“œκ°€ λ©λ‹ˆλ‹€.
  • asyncν•¨μˆ˜λŠ” 항상 ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ κ°•μ œν•©λ‹ˆλ‹€.
// function getNumber(): Promise<number>
async function getNumber() {
return 42;
}

async ν™”μ‚΄ν‘œ ν•¨μˆ˜λ₯Ό λ§Œλ“€ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

const getNumber = async () => 42; // νƒ€μž…μ΄ () => Promise<number>

ν”„λ‘œλ―ΈμŠ€λ₯Ό 직접 μƒμ„±ν•˜λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

const getNumber = () => Promise.resolve(42); // νƒ€μž…μ΄ () => Promise<number>

ν•¨μˆ˜λŠ” 항상 동기 λ˜λŠ” λΉ„λ™κΈ°λ‘œ μ‹€ν–‰λ˜μ–΄μ•Ό ν•˜λ©° μ ˆλŒ€ ν˜Όμš©ν•΄μ„œλŠ” μ•ˆ λ©λ‹ˆλ‹€. μ½œλ°±μ΄λ‚˜ ν”„λ‘œλ―ΈμŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ 반(half)동기 μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆμ§€λ§Œ, asyncλ₯Ό μ‚¬μš©ν•˜λ©΄ 항상 비동기 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” μ…ˆμž…λ‹ˆλ‹€.
async ν•¨μˆ˜μ—μ„œ ν”„λ‘œλ―ΈμŠ€λ₯Ό λ°˜ν™˜ν•˜λ©΄ 또 λ‹€λ₯Έ ν”„λ‘œλ―ΈμŠ€λ‘œ λž˜ν•‘λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λ°˜ν™˜ νƒ€μž…μ€ Promise<Promise<T>>κ°€ μ•„λ‹Œ Promise<T>κ°€ λ©λ‹ˆλ‹€. νƒ€μž…μŠ€ν¬λ¦½νŠΈλ₯Ό μ‚¬μš©ν•˜λ©΄ νƒ€μž… 정보가 λͺ…ν™•νžˆ λ“œλŸ¬λ‚˜κΈ° λ•Œλ¬Έμ— 비동기 μ½”λ“œμ˜ κ°œλ…μ„ μž‘λŠ” 데 도움이 λ©λ‹ˆλ‹€.

// function getJSON(url: string): Promise<any>
async function getJSON(url: string) {
const response = await fetch(url);
const jsonPromise = response.json(); // νƒ€μž…μ΄ Promise<any>
return jsonPromise;
}

πŸ₯• μ•„μ΄ν…œ 26. νƒ€μž… 좔둠에 λ¬Έλ§₯이 μ–΄λ–»κ²Œ μ‚¬μš©λ˜λŠ”μ§€ μ΄ν•΄ν•˜κΈ°β€‹

λ¬Έλ§₯을 κ³ λ €ν•΄ νƒ€μž…μ„ μΆ”λ‘ ν•˜λ©΄ 가끔 μ΄μƒν•œ κ²°κ³Όκ°€ λ‚˜μ˜΅λ‹ˆλ‹€.

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” μ½”λ“œμ˜ λ™μž‘κ³Ό μ‹€ν–‰ μˆœμ„œλ₯Ό 바꾸지 μ•ŠμœΌλ©΄μ„œ ν‘œν˜„μ‹μ„ μƒμˆ˜λ‘œ 뢄리해 λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μŒ 두 λ¬Έμž₯은 λ™μΌν•©λ‹ˆλ‹€.

// 인라인 ν˜•νƒœ
setLanguage('JavaScript');

// μ°Έμ‘° ν˜•νƒœ
let language = 'JavaScript';
setLanguage(language);

νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” λ‹€μŒ λ¦¬νŒ©ν„°λ§μ΄ μ—¬μ „νžˆ λ™μž‘ν•©λ‹ˆλ‹€.

function setLanguage(language: string) { /* ... */ }

setLanguage('JavaScript');

let language = 'JavaScript';
setLanguage(language);

이제 λ¬Έμžμ—΄ νƒ€μž…μ„ 더 νŠΉμ •ν•΄μ„œ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ νƒ€μž…μ˜ μœ λ‹ˆμ˜¨μœΌλ‘œ λ°”κΎΌλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

type Language = 'JavaScript' | 'TypeScript' | 'Python';
function setLanguage(language: Language) { /* ... */ }

setLanguage('JavaScript'); // 정상

let language = 'JavaScript';
setLanguage(language); // string ν˜•μ‹μ˜ μΈμˆ˜λŠ” Language ν˜•μ‹μ˜ λ§€κ°œλ³€μˆ˜μ— 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

인라인 ν˜•νƒœμ—μ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν•¨μˆ˜ 선언을 톡해 λ§€κ°œλ³€μˆ˜κ°€ Language νƒ€μž…μ΄μ–΄μ•Ό ν•œλ‹€λŠ” 것을 μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή νƒ€μž…μ— λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ JavaScriptλŠ” ν• λ‹Ή κ°€λŠ₯ν•˜λ―€λ‘œ μ •μƒμž…λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 이 값을 λ³€μˆ˜λ‘œ 뢄리해내면, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” ν• λ‹Ή μ‹œμ μ— νƒ€μž…μ„ μΆ”λ‘ ν•©λ‹ˆλ‹€. 이번 κ²½μš°λŠ” string으둜 μΆ”λ‘ ν–ˆκ³ , Language νƒ€μž…μœΌλ‘œ 할당이 λΆˆκ°€λŠ₯ν•˜λ―€λ‘œ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.

이런 문제λ₯Ό ν•΄κ²°ν•˜λŠ” 두 가지 방법이 μžˆμŠ΅λ‹ˆλ‹€. 첫 번째 해법은 νƒ€μž… μ„ μ–Έμ—μ„œ language의 κ°€λŠ₯ν•œ 값을 μ œν•œν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

let language: Language = 'JavaScript';
setLanguage(language); // 정상

두 번째 해법은 languageλ₯Ό μƒμˆ˜λ‘œ λ§Œλ“œλŠ” κ²ƒμž…λ‹ˆλ‹€.

const language = 'JavaScript';
setLanguage(language); // 정상

constλ₯Ό μ‚¬μš©ν•˜μ—¬ νƒ€μž… μ²΄μ»€μ—κ²Œ languageλŠ” λ³€κ²½ν•  수 μ—†λ‹€κ³  μ•Œλ € μ€λ‹ˆλ‹€. λ”°λΌμ„œ νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” language에 λŒ€ν•΄μ„œ 더 μ •ν™•ν•œ νƒ€μž…μΈ λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ JavaScript둜 μΆ”λ‘ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

그런데 이 κ³Όμ •μ—μ„œ μ‚¬μš©λ˜λŠ” λ¬Έλ§₯μœΌλ‘œλΆ€ν„° 값을 λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€. λ¬Έλ§₯κ³Ό 값을 λΆ„λ¦¬ν•˜λ©΄ 좔후에 근본적인 문제λ₯Ό λ°œμƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

νŠœν”Œ μ‚¬μš© μ‹œ μ£Όμ˜μ β€‹

이동이 κ°€λŠ₯ν•œ 지도λ₯Ό 보여 μ£ΌλŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•œλ‹€κ³  생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

// λ§€κ°œλ³€μˆ˜λŠ” (latitude, longitude) μŒμž…λ‹ˆλ‹€.
function panTo(where: [number, number]) { /* ... */ }

panTo([10, 20]); // 정상

const loc = [10, 20];
panTo(loc); // number[] ν˜•μ‹μ˜ μΈμˆ˜λŠ” [number, number] ν˜•μ‹μ˜ λ§€κ°œλ³€μˆ˜μ— 할당될 수 μ—†μŠ΅λ‹ˆλ‹€.

anyλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  였λ₯˜λ₯Ό κ³ μΉ  수 μžˆλŠ” 방법을 생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€. any λŒ€μ‹  const둜 μ„ μ–Έν•˜λ©΄ λœλ‹€λŠ” 닡이 λ– μ˜€λ₯Ό μˆ˜λ„ μžˆκ² μ§€λ§Œ loc은 이미 const둜 μ„ μ–Έν•œ μƒνƒœμž…λ‹ˆλ‹€. κ·Έλ³΄λ‹€λŠ” νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ μ˜λ„λ₯Ό μ •ν™•νžˆ νŒŒμ•…ν•  수 μžˆλ„λ‘ νƒ€μž… 선언을 μ œκ³΅ν•˜λŠ” 방법을 μ‹œλ„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

const loc: [number, number] = [10, 20];
panTo(loc); // 정상

anyλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  였λ₯˜λ₯Ό κ³ μΉ  수 μžˆλŠ” 또 λ‹€λ₯Έ 방법은 μƒμˆ˜ λ¬Έλ§₯을 μ œκ³΅ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. constλŠ” 단지 값이 κ°€λ¦¬ν‚€λŠ” μ°Έμ‘°κ°€ λ³€ν•˜μ§€ μ•ŠλŠ” 얕은 μƒμˆ˜μΈ 반면, as constλŠ” κ·Έ 값이 λ‚΄λΆ€κΉŒμ§€ μƒμˆ˜λΌλŠ” 사싀을 νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—κ²Œ μ•Œλ € μ€λ‹ˆλ‹€.

const loc = [10, 20] as const;
panTo(loc); // readonly [10, 20] ν˜•μ‹μ€ readonly이며 λ³€κ²½ κ°€λŠ₯ν•œ ν˜•μ‹ [number, number]에 ν• λ‹Ήν•  수 μ—†μŠ΅λ‹ˆλ‹€.

νŽΈμ§‘κΈ°μ—μ„œ loc에 마우슀λ₯Ό 올렀보면, νƒ€μž…μ€ 이제 number[]κ°€ μ•„λ‹ˆλΌ readonly[10, 20]으둜 좔둠됨을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 그런데 μ•ˆνƒ€κΉžκ²Œλ„ 이 좔둠은 panTo의 νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λŠ” where의 λ‚΄μš©μ΄ λΆˆλ³€μ΄λΌκ³  보μž₯ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 즉, loc λ§€κ°œλ³€μˆ˜κ°€ readonly νƒ€μž…μ΄λ―€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
λ”°λΌμ„œ anyλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  였λ₯˜λ₯Ό κ³ μΉ  수 μžˆλŠ” μ΅œμ„ μ˜ 해결책은 panTo ν•¨μˆ˜μ— readonly ꡬ문을 μΆ”κ°€ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

function panTo(where: readonly [number, number]) { /* ... */ }
const loc = [10, 20] as const;
panTo(loc); // 정상

νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λ₯Ό μˆ˜μ •ν•  수 μ—†λŠ” 경우라면 νƒ€μž… ꡬ문을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€.
as constλŠ” λ¬Έλ§₯ 손싀과 κ΄€λ ¨ν•œ 문제λ₯Ό κΉ”λ”ν•˜κ²Œ ν•΄κ²°ν•  수 μžˆμ§€λ§Œ, ν•œ 가지 단점을 가지고 μžˆμŠ΅λ‹ˆλ‹€. λ§Œμ•½ νƒ€μž… μ •μ˜μ— μ‹€μˆ˜κ°€ μžˆλ‹€λ©΄(예λ₯Ό λ“€μ–΄, νŠœν”Œμ— μ„Έ 번째 μš”μ†Œλ₯Ό μΆ”κ°€ν•œλ‹€λ©΄) 였λ₯˜λŠ” νƒ€μž… μ •μ˜κ°€ μ•„λ‹ˆλΌ ν˜ΈμΆœλ˜λŠ” κ³³μ—μ„œ λ°œμƒν•œλ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€. 특히 μ—¬λŸ¬ κ²Ή μ€‘μ²©λœ κ°μ²΄μ—μ„œ 였λ₯˜κ°€ λ°œμƒν•œλ‹€λ©΄ 근본적인 원인을 νŒŒμ•…ν•˜κΈ° μ–΄λ ΅μŠ΅λ‹ˆλ‹€.

const loc = [10, 20, 30] as const; // μ‹€μ œ 였λ₯˜λŠ” μ—¬κΈ°μ„œ λ°œμƒν•©λ‹ˆλ‹€.
panTo(loc); // μ—λŸ¬ λ‘œκ·Έκ°€ μ—¬κΈ°μ„œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€.

객체 μ‚¬μš© μ‹œ μ£Όμ˜μ β€‹

λ¬Έλ§₯μ—μ„œ 값을 λΆ„λ¦¬ν•˜λŠ” λ¬Έμ œλŠ” λ¬΄μžμ—΄ λ¦¬ν„°λŸ΄μ΄λ‚˜ νŠœν”Œμ„ ν¬ν•¨ν•˜λŠ” 큰 κ°μ²΄μ—μ„œ μƒμˆ˜λ₯Ό 뽑아낼 λ•Œλ„ λ°œμƒν•©λ‹ˆλ‹€.

type Language = 'JavaScript' | 'TypeScript' | 'Python';
interface GovernedLanguage {
language: Language;
organization: string;
}

function complain(language: GovernedLanguage) { /* ... */ }

complain({ language: 'TypeScript', organization: 'Microsoft' }); // 정상

const ts = {
language: 'TypeScript',
organization: 'Microsoft',
};

complain(ts); // μ—λŸ¬ λ°œμƒ

이 λ¬Έμ œλŠ” νƒ€μž… 선언을 μΆ”κ°€ν•˜κ±°λ‚˜ μƒμˆ˜ 단언을 μ‚¬μš©ν•΄ ν•΄κ²°ν•©λ‹ˆλ‹€.

콜백 μ‚¬μš© μ‹œ μ£Όμ˜μ β€‹

μ½œλ°±μ„ λ‹€λ₯Έ ν•¨μˆ˜λ‘œ 전달할 λ•Œ, νƒ€μž…μŠ€ν¬λ¦½νŠΈλŠ” 콜백의 λ§€κ°œλ³€μˆ˜ νƒ€μž…μ„ μΆ”λ‘ ν•˜κΈ° μœ„ν•΄ λ¬Έλ§₯을 μ‚¬μš©ν•©λ‹ˆλ‹€.

function callWithRandomNumbers(fn: (n1: number, n2: number) => void) {
fn(Math.random(), Math.random());
}

callWithRandomNumbers((a, b) => {
a; // νƒ€μž…μ΄ number
b; // νƒ€μž…μ΄ number
console.log(a + b);
});

callWithRandomNumbers의 νƒ€μž… μ„ μ–ΈμœΌλ‘œ 인해 a와 b의 νƒ€μž…μ΄ number둜 μΆ”λ‘ λ©λ‹ˆλ‹€. μ½œλ°±μ„ μƒμˆ˜λ‘œ 뽑아내면 λ¬Έλ§₯이 μ†Œμ‹€λ˜κ³  noImplicitAny 였λ₯˜κ°€ λ°œμƒν•˜κ²Œ λ©λ‹ˆλ‹€.

const fn = (a, b) => {
// a, b λ§€κ°œλ³€μˆ˜μ—λŠ” μ•”μ‹œμ μœΌλ‘œ any ν˜•μ‹μ΄ ν‘œν•¨λ©λ‹ˆλ‹€.
console.log(a + b);
}

callWithRandomNumbers(fn);

이런 κ²½μš°λŠ” λ§€κ°œλ³€μˆ˜μ— νƒ€μž… ꡬ문을 μΆ”κ°€ν•΄μ„œ ν•΄κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const fn = (a: number, b: number) => {
console.log(a + b);
}
callWithRandomNumbers(fn);

λ˜λŠ” κ°€λŠ₯ν•  경우 전체 ν•¨μˆ˜ ν‘œν˜„μ‹μ— νƒ€μž… 선언을 μ μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

πŸ₯• μ•„μ΄ν…œ 27. ν•¨μˆ˜ν˜• 기법과 라이브러리둜 νƒ€μž… 흐름 μœ μ§€ν•˜κΈ°β€‹

λ‘œλ°μ‹œλ‚˜ λžŒλ‹€ 같은 λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ˜ 일뢀 κ°€λŠ₯(map, flatMap, filter, reduce λ“±)은 순수 μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ κ΅¬ν˜„λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 기법은 루프λ₯Ό λŒ€μ²΄ν•  수 있기 λ•Œλ¬Έμ— μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œ μœ μš©ν•˜κ²Œ μ‚¬μš©λ˜λŠ”λ°, νƒ€μž…μŠ€ν¬λ¦½νŠΈμ™€ μ‘°ν•©ν•˜μ—¬ μ‚¬μš©ν•˜λ©΄ λ”μš± 빛을 λ°œν•©λ‹ˆλ‹€. κ·Έ μ΄μœ λŠ” νƒ€μž… 정보가 κ·ΈλŒ€λ‘œ μœ μ§€λ˜λ©΄μ„œ νƒ€μž… 흐름이 계속 μ „λ‹¬λ˜λ„λ‘ ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. λ°˜λ©΄μ— 직접 루프λ₯Ό κ΅¬ν˜„ν•˜λ©΄ νƒ€μž… 체크에 λŒ€ν•œ 관리도 직접 ν•΄μ•Ό ν•©λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄, μ–΄λ–€ CSV 데이터λ₯Ό νŒŒμ‹±ν•œλ‹€κ³  생각해 λ³΄κ² μŠ΅λ‹ˆλ‹€. 순수 μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” μ ˆμ°¨ν˜• ν”„λ‘œκ·Έλž˜λ° ν˜•νƒœλ‘œ κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

const csvData = "...";
const rawRows = csvData.split('/n');
const headers = rawRows[0].split(',');

const rows = rawRows.slice(1).map(rowStr => {
const row = {};
rowStr.split(',').forEach((val, j) => {
row[headers[j]] = val;
});
return row;
});

ν•¨μˆ˜ν˜• λ§ˆμΈλ“œλ₯Ό μ‘°κΈˆμ΄λΌλ„ 가진 μžλ°”μŠ€ν¬λ¦½νŠΈ 개발자라면 reduceλ₯Ό μ‚¬μš©ν•΄ ν–‰ 객체λ₯Ό λ§Œλ“œλŠ” 방법을 μ„ ν˜Έν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

const rows = rawRows.slice(1)
.map(rowStr => rowStr.split(',')
.reduce((row, val, i) => (row[headers[i]] = val, row), {}));

이 μ½”λ“œλŠ” μ ˆμ°¨ν˜• μ½”λ“œμ— λΉ„ν•΄ μ„Έ 쀄(μ•½ 20개의 κΈ€μž)을 μ ˆμ•½ν–ˆμ§€λ§Œ λ³΄λŠ” μ‚¬λžŒμ— 따라 더 λ³΅μž‘ν•˜κ²Œ 느껴질 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 킀와 κ°’ λ°°μ—΄λ‘œ μ·¨ν•©ν•΄μ„œ 객체둜 λ§Œλ“€μ–΄ μ£ΌλŠ”, λ‘œλŒ€μ‹œμ˜ zipObject ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜λ©΄ μ½”λ“œλ₯Ό λ”μš± 짧게 λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

import _ from 'lodash';
const rows = rawRows.slice(1)
.map(rowStr => _.zipObject(headers, rowStr.split(',')));

μ½”λ“œκ°€ 맀우 μ§§μ•„μ‘ŒμŠ΅λ‹ˆλ‹€. 그런데 μžλ°”μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” ν”„λ‘œμ νŠΈμ— μ„œλ“œνŒŒν‹° 라이브러리 쒅속성을 μΆ”κ°€ν•  λ•Œ 신쀑해야 ν•©λ‹ˆλ‹€. λ§Œμ•½ μ„œλ“œνŒŒν‹° 라이브러리 기반으둜 μ½”λ“œλ₯Ό 짧게 μ€„μ΄λŠ” 데 μ‹œκ°„μ΄ 많이 λ“ λ‹€λ©΄, μ„œλ“œνŒŒν‹° 라이브러리λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 게 λ‚«κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

κ·ΈλŸ¬λ‚˜ 같은 μ½”λ“œλ₯Ό νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μž‘μ„±ν•˜λ©΄ μ„œλ“œνŒŒν‹° 라이브러리λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 무쑰건 μœ λ¦¬ν•©λ‹ˆλ‹€. νƒ€μž… 정보λ₯Ό μ°Έκ³ ν•˜λ©° μž‘μ—…ν•  수 있기 λ•Œλ¬Έμ— μ„œλ“œνŒŒν‹° 라이브러리 기반으둜 λ°”κΎΈλŠ” 데 μ‹œκ°„μ΄ 훨씬 λ‹¨μΆ•λ©λ‹ˆλ‹€.

λ°μ΄ν„°μ˜ 가곡이 μ •κ΅ν•΄μ§ˆμˆ˜λ‘ μ΄λŸ¬ν•œ μž₯점은 λ”μš± λͺ…ν™•ν•΄μ§‘λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, λͺ¨λ“  NBA νŒ€μ˜ μ„ μˆ˜ λͺ…단을 가지고 μžˆλ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

interface BasketballPlayer {
name: string;
team: string;
salary: number;
}

declare const rosters: { [team: string]: BasketballPlayer[] };

λ₯΄ν”„λ₯Ό μ‚¬μš©ν•΄ λ‹¨μˆœ λͺ©λ‘μ„ λ§Œλ“€λ €λ©΄ 배열에 concat을 μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ‹€μŒ μ½”λ“œλŠ” λ™μž‘μ΄ λ˜μ§€λ§Œ νƒ€μž… μ²΄ν¬λŠ” λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

let allPlayers = [];

for (const players of Object.values(rosters)) {
allPlayers = allPlayers.concat(players);
// 'allPlayers' λ³€μˆ˜μ—λŠ” μ•”μ‹œμ μœΌλ‘œ 'any[]' ν˜•μ‹μ΄ ν¬ν•¨λ©λ‹ˆλ‹€.
}

이 였λ₯˜λ₯Ό 고치렀면 allPlayers에 νƒ€μž… ꡬ문을 μΆ”κ°€ν•΄μ•Ό ν•©λ‹ˆλ‹€.

let allPlayers: BasketballPlayer[] = [];
for (const players of Object.values(rosters)) {
allPlayers = allPlayers.concat(players); // 정상
}

κ·ΈλŸ¬λ‚˜ 더 λ‚˜μ€ 해법은 Array.prototype.flat을 μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

const allPlayers = Object.values(rosters).flat();

flat λ©”μ„œλ“œλŠ” 닀차원 배열을 평탄화해 μ€λ‹ˆλ‹€. νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λŠ” T[][] => T[] 같은 ν˜•νƒœμž…λ‹ˆλ‹€. 이 버전이 κ°€μž₯ κ°„κ²°ν•˜κ³  νƒ€μž… ꡬ문도 ν•„μš” μ—†μŠ΅λ‹ˆλ‹€. λ˜ν•œ allPlayers λ³€μˆ˜κ°€ ν–₯후에 λ³€κ²½λ˜μ§€ μ•Šλ„λ‘ let λŒ€μ‹  constλ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

allPlayersλ₯Ό 가지고 각 νŒ€λ³„λ‘œ 연봉 순으둜 μ •λ ¬ν•΄μ„œ 졜고 연봉 μ„ μˆ˜μ˜ λͺ…단을 λ§Œλ“ λ‹€κ³  κ°€μ •ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. λ‘œλŒ€μ‹œ μ—†λŠ” 방법은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€. ν•¨μˆ˜ν˜• 기법을 쓰지 μ•Šμ€ 뢀뢄은 νƒ€μž… ꡬ문이 ν•„μš”ν•©λ‹ˆλ‹€.

const teamToPlayers: {[team: string]: BasketballPlayer[]} = {};
for (const player of allPlayers) {
const { team } = player;
teamToPlayers[team] = teamToPlayers[team] || [];
teamToPlayers[team].push(player);
}

for (const players of Object.values(teamToPlayers)) {
players.sort((a, b) => b.salary - a.salary);
}

const bestPaid = Object.values(teamToPlayers).map(players => players[0]);
bestPaid.sort((playerA, playerB) => playerB.salary - playerA.salary);
console.log(bestPaid);

λ‘œλ°μ‹œλ₯Ό μ‚¬μš©ν•΄μ„œ λ™μΌν•œ μž‘μ—…μ„ ν•˜λŠ” μ½”λ“œλ₯Ό κ΅¬ν˜„ν•˜λ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

const bestPaid = _(allPlayers)
.groupBy(player => player.team)
.mapValues(players => _.maxBy(players, p => p.salary)!)
.values()
.sortBy(p => -p.salary)
.value();

길이가 절반으둜 μ€„μ—ˆκ³ , 보기에도 κΉ”λ”ν•˜λ©°, null이 μ•„λ‹˜ 단언문을 λ”± ν•œ 번만 μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ λ‘œλ°μ‹œμ™€ μ–Έλ”μŠ€μ½”μ–΄μ˜ κ°œλ…μΈ 체인을 μ‚¬μš©ν–ˆκΈ° λ•Œλ¬Έμ—, 더 μžμ—°μŠ€λŸ¬μš΄ μˆœμ„œλ‘œ 일련의 연산을 μž‘μ„±ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
그런데 λ‚΄μž₯된 Array.prototype.map λŒ€μ‹  _.map을 μ‚¬μš©ν•˜λ €λŠ” μ΄μœ λŠ” λ¬΄μ—‡μΌκΉŒμš”? ν•œ 가지 μ΄μœ λŠ” μ½œλ°±μ„ μ „λ‹¬ν•˜λŠ” λŒ€μ‹  μ†μ„±μ˜ 이름을 전달할 수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

λ‚΄μž₯된 ν•¨μˆ˜ν˜• 기법듀과 λ‘œλŒ€μ‹œ 같은 λΌμ΄λΈŒλŸ¬λ¦¬μ— νƒ€μž… 정보가 잘 μœ μ§€λ˜λŠ” 것은 μš°μ—°μ΄ μ•„λ‹™λ‹ˆλ‹€. ν•¨μˆ˜ 호좜 μ‹œ μ „λ‹¬λœ λ§€κ°œλ³€μˆ˜ 값을 κ±΄λ“œλ¦¬μ§€ μ•Šκ³  맀번 μƒˆλ‘œμš΄ 값을 λ°˜ν™˜ν•¨μœΌλ‘œμ¨, μƒˆλ‘œμš΄ νƒ€μž…μœΌλ‘œ μ•ˆμ „ν•˜κ²Œ λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.