2024-05-23.md
๐กDIL: ์ดํํฐ๋ธ ํ์ ์คํฌ๋ฆฝํธ
์คํฐ๋: ์๊ฐ CS, https://github.com/monthly-cs/2024-05-effective-typescript
์์ฑ์ผ: 2024-05-23
์์ฑ์: dusunax
์์ดํ 24: ์ผ๊ด์ฑ ์๋ ๋ณ์นญ ์ฌ์ฉํ๊ธฐ Be Consistent in Your Use of Aliases
- ๋ณ์นญ์ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ์๋ ์์ฑ๊ฐ์์๋ ๋ณ๊ฒฝ๋๋ค
- ์ ์ด ํ๋ฆ์ ๋ถ์ํ๊ธฐ ์ด๋ ต๋ค
const place = { name: "New York", latLng: [41.6868, -74.2692] };
const loc = place.latLng; // ๋ณ์นญ alias
์ ์ด ํ๋ฆ ์์, ๋น๊ตฌ์กฐํ
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;
}
}
// ... more complex check
}
function isPointInPolygon(polygon: Polygon, pt: Coordinate) {
const box = polygon.bbox; // ์์ ๋ณ์ + strictNullChecks
// box๋ผ๋ ๋ณ์นญ์ ๋ง๋ค์ด์ ์ ์ด ํ๋ฆ ๋ถ์์ ๋ฐฉํดํ๋ค
if (polygon.bbox) {
box.x[0]; // ์์ฑ ์ฒดํฌ ๋์ง ์์์
//~~~ 'box' is possibly 'undefined'
}
if (box) {
box.x[0]; // ํ์
์ฒด์ปค์ ๋ฌธ์ ์์ง๋ง. box์ bbox๋ผ๋ ๊ฐ์ ๊ฐ์ธ ๋ฐ ๋ค๋ฅธ ์ด๋ฆ์ ์ฌ์ฉํ๋ ๋ณ์๋ฅผ ๋ง๋ค์๋ค
}
const { bbox } = polygon; // ๊ฐ์ฒด ๋น๊ตฌ์กฐํ๋ฅผ ์ฌ์ฉํ๋ค.
if (bbox) {
const { x } = bbox; // ๋ฐฐ์ด, ์ค์ฒฉ๋ ๊ตฌ์กฐ์์๋ ์ฌ์ฉํ๋ค
// ๋ณด๋ค ๊ฐ๊ฒฐํ ๋ฌธ๋ฒ์ผ๋ก ์ผ๊ด๋ ์ด๋ฆ ์ฌ์ฉํ๊ธฐ
// ์ ํ์ ์์ฑ์ธ ๊ฒฝ์ฐ, ์์ฑ ์ฒดํฌ๊ฐ ๋ ํ์. ๊ฒฝ๊ณ์ null๊ฐ ์ถ๊ฐ
}
}
์ ์ด ํ๋ฆ ๋ถ์ Control Flow Analysis
- ์ง์ญ ๋ณ์์์๋ O. ๊ฐ์ฒด ์์ฑ์์๋ ์ฃผ์
- ์ง์ญ ๋ณ์๋ก ๋ฝ์์ ์ฌ์ฉํ๋ฉด? ๋ณ์์ ํ์ ์ ์ ์ง๋์ง๋ง ์๋ ๊ฐ์ฒด์ ๊ฐ๊ณผ ๊ฐ๊ฒ ์ ์ง๋์ง ์์ ์ ์์
const myObj = {
value: 10,
};
let val = myObj.value;
val += 5;
console.log(`๊ฐ์ฒด์ ๊ฐ: ${myObj.value}`); // ๊ฐ์ฒด์ ๊ฐ: 10
console.log(`๊ฐ: ${val}`); // ์ง์ญ ๋ณ์์ ๊ฐ: 15
Things to Remember
- Aliasing can prevent TypeScript from narrowing types. If you create an alias for a variable, use it consistently.
- ๋ณ์นญ์ ํ์ ์คํฌ๋ฆฝํธ๊ฐ ํ์ ์ ์ขํ๋ ๊ฒ์ ๋ฐฉํดํ๊ธฐ ๋๋ฌธ์~ ์ผ๊ด์ ์ธ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํด์ผ ํจ
- Be aware of how function calls can invalidate type refinements on properties. Trust refinements on local variables more than on properties.
- ํจ์์ ํธ์ถ์ด ๊ฐ์ฒด ์์ฑ์
ํ์ ์ ์ refinements
๋ฅผ ๋ฌดํจํ ํ ์ ์๋ค๋ ์ ์ ์ฃผ์ - ์์ฑ๋ณด๋ค ์ง์ญ ๋ณ์์ ํ์ ์ ์ ๋ฅผ ๋ฏฟ์ผ์ธ์ฉ
- ํจ์์ ํธ์ถ์ด ๊ฐ์ฒด ์์ฑ์
์์ดํ 25: ๋น๋๊ธฐ ์ฝ๋์๋ ์ฝ๋ฐฑ ๋์ async ํจ์ ์ฌ์ฉํ๊ธฐ Use async Functions Instead of Callbacks to Improve Type Flow
- ๋น๋๊ธฐ ๋์์ ๋ชจ๋ธ๋ง ํ๊ธฐ ์ํด ๋ง์ฃผํ๋ ์ฝ๋ฐฑ ์ง์ฅ ๐
- ES2015์ Promise ๋์
๐ (future๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํ๋ค?)
- (1)์ฝ๋ ์ค์ฒฉ์ ์ค์ด๊ณ , (2)์คํ ์์๋ฅผ ์ฝ๋ ์์์ ๊ฐ๊ฒ ํจ. (3)์ค๋ฅ ์ฒ๋ฆฌ๊ฐ ์ฝ๊ณ (4)Promise.all๊ณผ ๊ฐ์ ๊ธฐ๋ฒ ์ฌ์ฉํ ์ ์์
async function
- await ํค์๋๋ Promise๊ฐ resolve(์ฒ๋ฆฌ)๋ ๋๊น์ง ํด๋น ํจ์์ ์คํ์ ๋ฉ์ถค
- async ํจ์ ๋ด์์ await ์ค์ธ Promise๊ฐ reject(๊ฑฐ์ )๋๋ฉด ์์ธ๋ฅผ ๋์ง๋ค
- try/catch
async function & typescipt
- callback๋ณด๋ค promise๋ฅผ ์ฌ์ฉํ ์ด์
- ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ๋ ์ฝ๋ค
- ํ์ ์ ์ถ๋ก ํ๊ธฐ ๋ ์ฝ๋ค
- Promise.all
- awiat๊ณผ ๊ตฌ์กฐ ๋ถํด ํ ๋น
// ํ๋ก๋ฏธ์ค
async function fetchPages() {
// ๊ฐ response์ ํ์
์ ์ถ๋ก
const [response1, response2, response3] = await Promise.all([
fetch(url1),
fetch(url2),
fetch(url3),
]);
}
// ์ฝ๋ฐฑ ํจ์
function fetchPagesWithCallbacks() {
let numDone = 0;
const responses: string[] = [];
const done = () => {
const [response1, response2, response3] = responses;
};
const urls = [url1, url2, url3];
urls.forEach((url, i) => {
fetchURL(url, (r) => {
responses[i] = url;
numDone++;
if (numDone === urls.length) done();
});
});
// ์ฝ๋์ ํ์
๊ตฌ๋ฌธ์ ๋ ๋ง์ด ์จ์ผํ๋ค
}
Promise.race
- ํ์์์์ ์ถ๊ฐํ ํจํด
/** ์๊ฐ ๋ด์ HTTP ์์ฒญ์ ์๋ฃํ์ง ๋ชปํ๋ฉด "timeout"์ timeoutMs์ ํจ๊ป ๋ฆฌํดํ๋ ํจ์ */
function timeout(timeoutMs: number): Promise<never> {
return new Promise((resolve, reject) => {
setTimeout(() => reject("timeout"), timeoutMs);
});
}
// (ํ์
) Promise<Response>๋ก ์ถ๋ก ๋๋ค
// Promise<Reponse | never>๋??
// ๊ณต์งํฉ(never)์ ์ ๋์จ์ ์๋ฌด๋ฐ ํจ๊ณผ๊ฐ ์์ผ๋ฏ๋ก => Promise<Response>๋ค.
async function fetchWithTimeout(url: string, timeoutMs: number) {
return Promise.race([fetch(url), timeout(timeoutMs)]); // ๋ ์ค ๋๊ฐ ๋จผ์ ์๋ฃ๋ ๊ฒ์ธ๊ฐ?
// url๊ณผ ํ์์์ ๊ฐ์ ๋ฐ์, ์ง์ ๋ ์๊ฐ ๋ด HTTP ์์ฒญ์ ์๋ฃํ์ง ๋ชปํ๋ฉด ์๋ฌ ๋ฐ์
// ๋ง์ฝ fetch๊ฐ ๋จผ์ ์๋ฃ๋๋ฉด? ๊ฒฐ๊ณผ ๋ฐํ
// ๋ง์ฝ timout์ด ๋จผ์ ์๋ฃ๋๋ฉด? 'timeout' ์๋ฌ ๋ฐํ
}
new Promise๋ณด๋ค async function ์ฌ์ฉํ๊ธฐ
- ํ๋ก๋ฏธ์ค๋ฅผ ์์ฑํ๊ธฐ๋ณด๋ค, async/await๋ฅผ ์ฌ์ฉํ ์ ์๋ค
- async ํจ์๋ Promise๋ฅผ ๋ฐํํ๋ค!
// async ํ์ดํ ํจ์ ๋ง๋ค๊ธฐ
const getNumber = async () => 42;
// ^? const getNumber: () => Promise<number>
// ํ๋ก๋ฏธ์ค๋ฅผ ์ง์ ์์ฑํ๋ค๋ฉด
const getNumber = () => Promise.resolve(42);
// ^? const getNumber: () => Promise<number>
- ์ฆ์ ์ฌ์ฉ๊ฐ๋ฅํ ๊ฐ์์๋ ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํ?
- ํจ์๋ ๋๊ธฐ or ๋น๋๊ธฐ๋ก ์คํ / ํผ์ฉํ์ง ์๊ธฐ (๊ด๋ฆฌํ๊ธฐ ํฌ์ด ๋๋ค, async () => Promise<Promise>)
- ์ค๋ณต ํ๋ก๋ฏธ์ค ๋ํx, ๋น๋๊ธฐ ์ฝ๋์ ๊ฐ๋ ์ก๊ธฐ~
const _cache: { [url: string]: string } = {};
async function fetchWithCache(url: string) {
if (url in _cache) {
return _cache[url];
}
const response = await fetch(url); // ์ผ๊ด์ ์ธ ๋์ ๊ฐ์
const text = await response.text(); // await!
_cache[url] = text;
return text;
}
let requestStatus: "loading" | "success" | "error";
async function getUser(userId: string) {
requestStatus = "loading";
const profile = await fetchWithCache(`/user/${userId}`);
requestStatus = "success";
}
Things to Remember
- Prefer Promises to callbacks for better composability and type flow.
- Promise๊ฐ callback๋ณด๋ค ์ฝ๋ ์์ฑ๊ณผ, ํ์ ์ถ๋ก ๋ฉด์์ ์ ๋ฆฌํ๋ค.
- Prefer async and await to raw Promises when possible. They produce more concise, straightforward code and eliminate whole classes of errors.
- ๊ฐ๊ฒฐํ๊ณ ์ง๊ด์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด ์ง์ Promise๋ฅผ ์ฌ์ฉํ์ง ์๊ณ , async/await์ ์ฌ์ฉํ์
- If a function returns a Promise, declare it async.
- ํ๋ก๋ฏธ์ค๋ฅผ ๋ฐํํ๋ค๋ฉด async๋ก ์ ์ธํ๊ธฐ