2024-06-04.md
๐กDIL: ์ดํํฐ๋ธ ํ์ ์คํฌ๋ฆฝํธ
์คํฐ๋: ์๊ฐ CS, https://github.com/monthly-cs/2024-05-effective-typescript
์์ฑ์ผ: 2024-06-04
์์ฑ์: dusunax
์์ดํ
40: ํจ์ ์์ผ๋ก ํ์
๋จ์ธ๋ฌธ ๊ฐ์ถ๊ธฐ Hide Unsafe Type Assertions in Well-Typed Functions
- ์ธ๋ถ ํ์
์ ์๋ ๊ฐ๋จํ์ง๋ง, ํจ์ ๋ด๋ถ ๋ก์ง์ด ๋ณต์กํ ๊ฒฝ์ฐ!
- ๋ด๋ถ์ ํ์ ๋จ์ธ์ ์ฌ์ฉํ๊ณ , ์ธ๋ถ๋ก ๋๋ฌ๋๋ ํ์ ์ ์๋ฅผ ์ ํํ ๋ช ์
- ํ๋ก์ ํธ์ ํ์ ๋จ์ธ๋ฌธ์ด ๋๋ฌ๋ ์์ง ์๋๋ก ๊ฐ์ถ๊ธฐ
์บ์ ๋ํผ ํจ์
๋ฆฌ์กํธ๋ useMemo๊ฐ ์๋ค.
- ํจ์ ๋ด๋ถ์ any๊ฐ ๋ง์ง๋ง, cacheLast์ ํ์
์ ์์๋ any๊ฐ ์๊ธฐ ๋๋ฌธ์, cacheLast๋ฅผ ํธ์ถํ๋ ์ชฝ์์๋ any๊ฐ ์ฌ์ฉ๋์๋์ง ์์ง ๋ชปํ๋ค.
- any๋ฅผ ์ฌ์ฉํ์ง๋ง, cacheLast๋ฅผ ์ฌ์ฉํ๋ ์ชฝ์์๋ ํ์ ์์ ์ฑ์ ์ ์งํจ
declare function cacheLast<T extends Function>(fn: T): T;
declare function shallowEqual(a: any, b: any): boolean;
// ๊ตฌํ์ฒด
// ์๋ณธ ํจ์๊ฐ ๊ฐ์ฒด์ฒ๋ผ ์์ฑ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ค๋ฉด ํ์
์ด ๋ฌ๋ผ์ง๋ค
// (์ฐ์์ ์ผ๋ก ํธ์ถํ ๋ this ๊ฐ์ด ๋์ผํ ์ง ์ฒดํฌํ์ง ์์)
function cacheLast<T extends Function>(fn: T): T {
let lastArgs: any[] | null = null;
let lastResult: any;
return function (...args: any[]) {
if (!lastArgs || !shallowEqual(lastArgs, args)) {
lastResult = fn(...args); // ๋ง์ง๋ง ํจ์ ๋ฐํ๊ฐ
lastArgs = args; // ๋ง์ง๋ง ๋งค๊ฐ๋ณ์
}
return lastResult;
}; as unknown as T;
}
// this๋ฅผ ์ถ๊ฐํ ๊ฒฝ์ฐ
function cacheLastWithThis<T extends Function>(fn: T): T {
let lastArgs: any[] | null = null;
let lastResult: any;
let lastContext: any; // lastContext ๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ ๋ง์ง๋ง ํธ์ถ ์์ this ๊ฐ์ ์ ์ฅ
return function (this: any, ...args: any[]) {
if (!lastArgs || lastContext !== this || !shallowEqual(lastArgs, args)) {
lastResult = fn.apply(this, args); // ๋ง์ง๋ง ํจ์ ๋ฐํ๊ฐ
lastArgs = args; // ๋ง์ง๋ง ๋งค๊ฐ๋ณ์
lastContext = this; // ๋ง์ง๋ง this ๊ฐ
}
return lastResult;
} as unknown as T;
}
shallowObjectEqual
- ๊ฐ์ฒด๊ฐ ๊ฐ์ ์ง ์ฒดํฌํ๊ธฐ ์ํด
- ์๋์
(b as any)[key]
๋จ์ธ์ ์์ ํ๋ค.key in b
(ํค๊ฐ ๊ฐ์ฒด b์ ์๋์ง)๋ฅผ ๋จผ์ ํ์ธํ๊ธฐ ๋๋ฌธ
function shallowObjectEqual<T extends object>(a: T, b: T): boolean {
// a, b: ๋น๊ตํ ๊ฐ์ฒด
for (const [key, value] of Object.entries(a)) {
// ๊ฐ์ฒด `a`์ ๋ชจ๋ ํค ๊ฐ ์์ ์ํ
if (!(key in b) || value !== (b as any)[key]) {
// ๊ฐ์ฒด b์, ํ์ฌ ํค๊ฐ ์กด์ฌํ๋ ์ง ํ์ธ.
// ํค๊ฐ ์๊ฑฐ๋, ๊ฐ์ฒด a์ ๊ฐ value๊ณผ ๋ค๋ฅด๋ค๋ฉด? false๋ฅผ ๋ฐํํ๋ค.
return false;
}
}
return Object.keys(a).length === Object.keys(b).length;
// ๋ ๊ฐ์ฒด์ ํค ๊ฐฏ์๊ฐ ๊ฐ์ ์ง ์ฒดํฌ
// ๋ชจ๋ ํค์ ๊ฐ์ด ์ผ์นํ๊ณ , ํค ๊ฐ์๊ฐ ๋์ผํ๋ค -> true (์์ ๋น๊ต๊ฐ ์ฐธ)
}
Things to Remember
- Sometimes unsafe type assertions and any types are necessary or expedient. When you need to use one, hide it inside a function with a correct signature.
- ๋๋๋ก ๋ถ๊ฐํผํ๊ฑฐ๋ ํธ์๋ฅผ ์ํด any๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ์ด ๋ any๋ฅผ ์ฌ๋ฐ๋ฅธ ์๊ทธ๋์ฒ์ ํจ์ ์์ ์จ๊ธฐ์. ํจ์ ๋ฐ๊นฅ์ ํ์ ์์ ์ฑ ์ ์ง
- Don't compromise a function's type signature to fix type errors in the implementation.
- ํ์ ์๋ฌ๋ฅผ ์์ ํ๊ธฐ ์ํด, ํจ์์ ํ์ ์๊ทธ๋์ฒ๋ฅผ ๋ฐ๊พธ์ง ๋ง์. (ํจ์์ ํ์ ์๊ทธ๋์ฒ๋ฅผ ์ ์งํ๋ฉด์ ํ์ ์ค๋ฅ๋ฅผ ํด๊ฒฐํ๊ธฐ)
- Make sure you explain why your type assertions are valid, and unit test your code thoroughly.
- ํ์ ๋จ์ธ์ ๋ํ ์ค๋ช ๊ณผ ์ ๋ ํ ์คํธ ํ์