2024-05-28.md
๐กDIL: ์ดํํฐ๋ธ ํ์ ์คํฌ๋ฆฝํธ
์คํฐ๋: ์๊ฐ CS, https://github.com/monthly-cs/2024-05-effective-typescript
์์ฑ์ผ: 2024-05-28
์์ฑ์: dusunax
์์ดํ 31: ํ์ ์ฃผ๋ณ์ null ๊ฐ ๋ฐฐ์นํ๊ธฐ Push Null Values to the Perimeter of Your Types
์์: null ๊ด๋ จ๋ ๊ด๊ณ๋ฅผ ๊ฐ์ง ๊ฒฐ๊ณผ๊ฐ ๋ฆฌํด
- ๋ฌธ์ ์ฝ๋
// @strictNullChecks: false
function extent(nums: Iterable<number>) {
// num๋ฐฐ์ด์ด ๋น์ด์์ ์ ์์
let min, max; // undefined, undefined
for (const num of nums) {
if (!min) {
// falsy๊ฐ ์๋๋ผ undefined๋ฅผ ์ฒดํฌํด์ผ ํ๋ค
// => 0์ผ๋ ๋ง์์์ง
min = num;
max = num;
} else {
min = Math.min(min, num);
max = Math.max(max, num);
}
}
return [min, max];
}
- ๊ฐ์ ์ฝ๋
function extent(nums: Iterable<number>) {
let minMax: [number, number] | null = null;
// ์ฌ์ฉํ๊ธฐ ๋ ์์ํ ์ฝ๋
// ํ์
์คํฌ๋ฆฝํธ๊ฐ null ๊ฐ ์ฌ์ด์ ๊ด๊ณ๋ฅผ ์ดํดํ ์ ์๋๋ก ํ๋ค (๋ ๊ฐ์ด ๋ค ์กด์ฌํ๊ฑฐ๋, null์)
// ๋จ์ผ ๊ฐ์ฒด๋ก ์ค๊ณ๋ฅผ ๊ฐ์ ํ๋ค
// null์ฒดํฌ๊ฐ ์ ๋๋ก ๋์ํ๋ค
for (const num of nums) {
if (!minMax) { // null์ด๋!
minMax = [num, num];
} else {
const [oldMin, oldMax] = minMax;
minMax = [Math.min(num, oldMin), Math.max(num, oldMax)];
}
}
return minMax;
}
...
const [min, max] = extent([0, 1, 2])!; // null ๋จ์ธ ์์ ํ์!
const span = max - min; // ์ ์
...
const range = extent([0, 1, 2]);
if (range) { // null ์ฒดํฌ~
const [min, max] = range;
const span = max - min; // ์ ์
}
null๊ณผ null์ด ์๋ ๊ฐ์ ๋น๋น๋ฐฅ
- ๋คํธ์ํฌ ์์ฒญ์ ๊ฒฝ์ฐ?
- ํ์ํ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋ ๋ค์ ํด๋์ค๋ฅผ ๋ง๋ ๋ค(๋ฐ์ดํฐ๋ฅผ ๊ฐ๊ณตํ๋ค)
- null ์ํ์ ๋ํ ๊ฒฝ์ฐ์ ์๊ฐ ๋ง๋ค๋ฉด?? => null ์ฒดํฌ ๋๋ฌด, ๋ฒ๊ทธ ์์ฐ
- null์ธ ๊ฒฝ์ฐ๊ฐ ํ์ํ ์์ฑ์ Promise๋ก ๋ฐ๊พธ๋ฉด ์๋๋ค
- ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ , ๋ชจ๋ ๋ฉ์๋๊ฐ ๋น๋๊ธฐ๋ก ๋ฐ๋์ด์ผ ํ๋ค
- ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ์ฝ๋๋ฅผ ๋จ์ํ๊ฒ ํ์ง๋ง ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ๋ ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ธฐ๋ ํ๋ค
์์
๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ์ฝ๋
interface UserProfile {
id: number;
name: string;
email: string | null; // ์ด๋ฉ์ผ์ด null์ผ ์ ์์
}
// ๐ ์ฌ์ฉ์ ํ๋กํ์ ๋น๋๊ธฐ์ ์ผ๋ก ๋ฐํํ๋ async ํจ์
// Promise๋ฅผ ๋ฐํ
async function getUserProfileAsync(userId: number): Promise<UserProfile> {
const users: UserProfile[] = [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bibim@example.com" },
{ id: 3, name: "Jack", email: null },
];
return users.find((user) => user.id === userId); // undefined ์ธ ๊ฒ๋ถํฐ ์๋ฌ
// {id: -1, name: null, email: null} ๊ฐ์ด ์ด๊ธฐ๊ฐ ๋ง๋ค๋ฉด? ๋ณต์กํด์ง
// `|| null`๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์ฝ๋๋ฅผ ๋จ์ํํ๊ณ , ๊ฐ๋ฐ์์ ํ์
์ฒด์ปค๊ฐ ์ฐ๊ด๊ด๊ณ๋ฅผ ์ดํดํ๊ธฐ ์ฝ๋๋ก ํ๋ค.
}
(async function () {
const userB = await getUserProfileAsync(3);
if (userB?.email) {
console.log(`User email: ${userB.email}`);
} else {
console.log("User email not available");
}
})();
Things to Remember
- Avoid designs in which one value being null or not null is implicitly related to another value being null or not null.
- ํ ๊ฐ์ null ์ฌ๋ถ๊ฐ ๋ค๋ฅธ ๊ฐ์ null ์ฌ๋ถ์ ์์์ ์ผ๋ก ๊ด๋ จ๋๋๋ก ์์ฑํ์ง ๋ง์ (์ฐ๋ฆฌ๋ ์ ์์)
- Push null values to the perimeter of your API by making larger objects either null or fully non-null. This will make code clearer both for human readers and for the type checker.
- API ์์ฑ ์์๋, ๋ฐํ ํ์ ์ ํฐ ๊ฐ์ฒด๋ก ๋ง๋ค์ด ์ ์ฒด๊ฐ null์ด๊ฑฐ๋ null์ด ์๋๋๋ก ์์ฑํด ๋ช ๋ฃํ๊ฒ ํ์.
- Consider creating a fully non-null class and constructing it when all values are available.
- ๋ชจ๋ ๊ฐ์ด ์ค๋น ๋์์ ๋, ํด๋์ค ์์ฑํ๋๋ก ํ์.
๐ Using fully non-null APIs and classes is preferable to using null values in them.
``