JavaScript Programming

Type Predicateλ₯Ό ν™œμš©ν•΄ 사내 λ‚΄μž₯ μœ ν‹Έν•¨μˆ˜ νƒ€μž… κ°€λ“œ 적용

ν”„λ‘œκ·Έλž˜λ¨Έ μ˜€μ›” 2025. 11. 15.

사내 Coreμ—μ„œ μ œκ³΅λ˜λŠ” λ‚΄μž₯ μœ ν‹Έ ν•¨μˆ˜ _.vIsEmpty()와 _.vIsNull()은 λŸ°νƒ€μž„μ—μ„œ null-safe 처리λ₯Ό μœ„ν•΄ 맀우 빈번히 μ‚¬μš©λ˜λŠ” ν•¨μˆ˜μž…λ‹ˆλ‹€. 

ν•˜μ§€λ§Œ μ΄λ ‡κ²Œ 빈번히 μ‚¬μš©λ˜λŠ” ν•¨μˆ˜κ°€ 컴파일 μ‹œμ μ—μ„  TypeScript ν™˜κ²½μ—μ„œ νƒ€μž… κ°€λ“œ κΈ°λŠ₯을 ν•˜μ§€ λͺ»ν•œλ‹€λŠ” 문제점이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

이런 λ¬Έμ œμ μ— μ˜κ΅¬μ‹¬κ³Ό λΆˆνŽΈν•¨μ„ 느끼며 κ°œμ„ μ„ ν•΄λ³΄κ³ μž 생각을 ν–ˆκ³ , 이번 글은 "κΈ°μ‘΄ ν•¨μˆ˜λ₯Ό λœ―μ–΄κ³ μΉ˜μ§€ μ•Šκ³ λ„ Type Guardλ₯Ό κ°€λŠ₯ν•˜κ²Œ λ§Œλ“  κ²½ν—˜"을 μ •λ¦¬ν•œ 개발 κΈ°λ‘μž…λ‹ˆλ‹€.

 

이번 κ²½ν—˜μ„ 톡해 Type Predicate 에 λŒ€ν•΄ 곡뢀할 수 μžˆμ—ˆκ³ ,

νƒ€μž… κ°€λ“œ 문제 ν•΄κ²°κ³Ό κΈ°μ‘΄ μ‹œμŠ€ν…œμ˜ κ°œμ„  μ˜μ§€μ— λŒ€ν•΄ λ³ΈλΆ€μž₯λ‹˜κ»˜λ„ μΉ­μ°¬ λ°›λŠ” 쒋은 κ²½ν—˜μ΄ λμŠ΅λ‹ˆλ‹€.😎😚

 

 

 

πŸͺ§μ„ μˆ˜ 지식 - Type Guard

 

TypeScriptμ—μ„œ νƒ€μž… κ°€λ“œ(Type Guard)λŠ” λŸ°νƒ€μž„ μ‹œμ μ— νŠΉμ • κ°’μ˜ νƒ€μž…을 μ’ν˜€μ„œ(Type Narrowing) μ»΄νŒŒμΌλŸ¬κ°€ “이 μ‹œμ μ—λŠ” μ΄ νƒ€μž…이 ν™•μ‹€ν•˜λ‹€”라고 νŒλ‹¨ν•  μˆ˜ μžˆκ²Œ ν•΄μ£ΌλŠ” λ¬Έλ²•μž…λ‹ˆλ‹€.

TypeScriptλŠ” 정적 νƒ€μž… μ–Έμ–΄μ΄μ§€λ§Œ, λŸ°νƒ€μž„μ—λŠ” νƒ€μž… 정보가 μ‚¬λΌμ§‘λ‹ˆλ‹€.
κ·Έλž˜μ„œ if λ¬Έμ΄λ‚˜ typeof, instanceof λ“±μ„ μ΄μš©ν•΄ κ°’μ˜ νƒ€μž…을 κ²€μ‚¬ν•˜λ©΄, μ»΄νŒŒμΌλŸ¬κ°€ κ·Έ μ‘°κ±΄ μ•ˆμ—μ„œλŠ” νƒ€μž…을 μ’ν˜€μ€λ‹ˆλ‹€.

 

// 1️⃣ typeof λ₯Ό μ΄μš©ν•œ νƒ€μž… κ°€λ“œ μ˜ˆμ‹œ
function printLength(x: string | number) {
  if (typeof x === "string") {
    console.log(x.length);   // βœ… string 으둜 μ’ν˜€μ§
  } else {
    console.log(x.toFixed()); // βœ… number 둜 μ’ν˜€μ§
  }
}

// 2️⃣ instanceof λ₯Ό μ΄μš©ν•œ νƒ€μž… κ°€λ“œ μ˜ˆμ‹œ
class Dog { bark() {} }
class Cat { meow() {} }

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();  // βœ… Dog νƒ€μž…μœΌλ‘œ μ’ν˜€μ§
  } else {
    animal.meow();  // βœ… Cat νƒ€μž…μœΌλ‘œ μ’ν˜€μ§
  }
}

//3️⃣ in μ—°μ‚°μžλ₯Ό μ΄μš©ν•œ νƒ€μž… κ°€λ“œ  μ˜ˆμ‹œ
type User = { name: string };
type Admin = { name: string; role: string };

function printInfo(person: User | Admin) {
  if ("role" in person) {
    console.log(person.role); // βœ… Admin νƒ€μž…μœΌλ‘œ μ’ν˜€μ§
  } else {
    console.log(person.name); // βœ… User νƒ€μž…μœΌλ‘œ μ’ν˜€μ§
  }
}

//4️⃣ κ°„λ‹¨ν•œ μžλ°”μŠ€ν¬λ¦½νŠΈμ˜ “truthy/falsy” 평가λ₯Ό μ΄μš©ν•œ κ°„λ‹¨ν•œ νƒ€μž… κ°€λ“œ μ˜ˆμ‹œ
type User = { name: string } | undefined;

function greet(user: User) {
  if (user) {
    // μ—¬κΈ°μ„œλŠ” userκ°€ User둜 μ’ν˜€μ§ (undefined μ•„λ‹˜)
    console.log("Hello, " + user.name);
  } else {
    console.log("No user");
  }
}

 

νƒ€μž… κ°€λ“œ(Type Guard)λŠ” μ‘°κ±΄λ¬Έμ„ ν†΅ν•΄ λ³€μˆ˜μ˜ νƒ€μž…을 μ’ν˜€(Type Narrowing) TypeScript μ»΄νŒŒμΌλŸ¬κ°€ μ•ˆμ „ν•˜κ²Œ νƒ€μž… μΆ”둠을 ν•  μˆ˜ μžˆλ„둝 λ•λŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

 

 

 

 

πŸ“λ°°κ²½ 상황

사내 Coreμ—μ„œ μ œκ³΅λ˜λŠ” vIsEmpty()λŠ” κ°’이 λΉ„μ–΄μžˆλŠ”μ§€λ₯Ό νŒλ³„ν•˜λŠ” λ‚΄μž₯ μœ ν‹Έ ν•¨μˆ˜μž…λ‹ˆλ‹€.
ν•¨μˆ˜μ˜ νƒ€μž… μ‹œκ·Έλ‹ˆμ²˜λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

/**
 * @example
 *     true
 *     _.vIsEmpty('undefined'); //=> true
 *     _.vIsEmpty(undefined); //=> true
 *     _.vIsEmpty(null); //=> true
 *     _.vIsEmpty(""); //=> true
 *     _.vIsEmpty([]); //=> true
 *     _.vIsEmpty({}); //=> true
 *
 *     false
 *     _.vIsEmpty("mscorlib"); //=> false
 *     _.vIsEmpty([1,2]); //=> false
 *     _.vIsEmpty(0); //=> false
 *     _.vIsEmpty(1.1235); //=> false
 *     _.vIsEmpty(true); //=> false
 *     _.vIsEmpty(false); //=> false
 */
function vIsEmpty(value: any): boolean;

 

vIsEmpty ν•¨μˆ˜λŠ” λŸ°νƒ€μž„μ—μ„œλŠ” μ•„μ£Ό ν›Œλ₯­ν•©λ‹ˆλ‹€.

  • undefined, null, λΉˆ λ°°μ—΄, λΉˆ κ°μ²΄κΉŒμ§€ μ²΄ν¬
  • μ„œλ²„ μ‚¬μ΄λ“œ λŸ°νƒ€μž„μ—μ„œ NPE λ°©μ§€ κ°€λ“œ μ—­ν• 

ν•˜μ§€λ§Œ TypeScript ν™˜κ²½μ—μ„œλŠ” μ „ν˜€ νƒ€μž…을 μ’ν˜€μ£Όμ§€ λͺ»ν•˜λŠ” ν•œκ³„점이 μžˆμŠ΅λ‹ˆλ‹€.

 

 

λ”°λΌμ„œ μ•„λž˜μ™€ 같이 μ‚¬μš©ν•˜μ—¬ νƒ€μž… 검사λ₯Ό ν”Όν•΄μ•Όλ§Œ ν–ˆμŠ΅λ‹ˆλ‹€.

// 1️⃣ JS의 “truthy/falsy” 평가λ₯Ό ν•œλ²ˆ 더 μ΄μš©ν•˜μ—¬ νƒ€μž…κ°€λ“œν•˜κΈ° 
if (!_.vIsEmpty(request.additional_info)) {
	if (request.additional_info) {
		const a = request.additional_info.a;
	}
}

// 2️⃣ Non-null 단언 μ—°μ‚°μž μ‚¬μš©ν•˜κΈ°(!)
if (!_.vIsEmpty(request.additional_info)) {
	const a = request.additional_info!.a;
}

 

 

1번 방식은 쑰건문을 ν•œλ²ˆ 더 μ‚¬μš©ν•˜κ²Œ λ˜λ©΄μ„œ μ½”λ“œ 가독성도 λ–¨μ–΄μ§€κ³ , λΉ„νš¨μœ¨μ μœΌλ‘œ λ³΄μž…λ‹ˆλ‹€.

 

2번 방식은 λŸ°νƒ€μž„μƒμ—μ„   null-safe ν•˜μ§€λ§Œ, 컴파일 μ‹œμ μ—μ„œ λ³΄κΈ°μ—λŠ” null-safe ν•˜μ§€ μ•Šλ‹€κ³  λŠκΌˆμŠ΅λ‹ˆλ‹€.(개발자만 μ•Ž, μ»΄ν“¨ν„°λŠ” λͺ¨λ¦„.) μ‹€λ¬΄μ—μ„œ μ ˆλŒ€μ μ΄κ³  κ°•λ ₯ν•œ 무기인 " ! "λŠ”,컴파일러의 νƒ€μž…κ²€μ¦μ„ μš°νšŒν•˜λŠ” 것일 뿐, 해결책이 μ•„λ‹™λ‹ˆλ‹€.

 

 

"μ•„ κ·ΈλŸΌ _.vIsEmpty μ“°μ§€λ§κ³  λͺ¨λ‘ if(객체) λ‘œ μ¨μ•Ό κ² λ‹€" 라고 μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ΄κ²ƒ λ˜ν•œ μ’‹μ€ ν•΄λ‹΅μ€ μ•„λ‹ˆμ—ˆμŠ΅λ‹ˆλ‹€.

vIsEmpty λ₯Ό λͺ»μ‚¬μš©ν•˜κ²Œ λ˜λ©΄μ„œ, vIsEmpty ν•¨μˆ˜κ°€ μ œκ³΅ν•΄μ£ΌλŠ” μ—¬λŸ¬ 검사λ₯Ό ν™œμš©ν•˜μ§€ λͺ»ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 

 

κ²°κ΅­ μ°œμ°œν•˜μ§€λ§Œ, 2λ²ˆλ°©μ‹μ„ νƒν•΄μ„œ μ‚¬μš©μ„ ν–ˆμŠ΅λ‹ˆλ‹€. (κ·Έλž˜λ„ λŸ°νƒ€μž„μ‹œμ μ—” null-safe ν•œ 건 λ§žμœΌλ‹ˆκΉ)

이런 λΆˆνŽΈν•¨μ„ 계속 κ²ͺ으며, vIsEmpty ν•¨μˆ˜κ°€ μ›λž˜ μ œκ³΅ν•˜λ˜ κΈ°λŠ₯은 μœ μ§€ν•œ 채 νƒ€μž… κ°€λ“œκ°€ 되게 λ¦¬νŒ©ν† λ§μ„ μ‹œλ„ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

 

 

 

✨문제 ν•΄κ²°

μ„€μ •νŒŒμΌ μˆ˜μ •?

첫번째둜 Core μ—μ„œ μ œκ³΅ν•˜λŠ” λ‚΄μž₯ ν•¨μˆ˜λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³ , μ„€μ •νŒŒμΌ λ“± μ™ΈλΆ€ μš”μ†Œλ₯Ό λ³€κ²½ν•˜μ—¬ νƒ€μž… κ°€λ“œκ°€ 될 수 있게 ν•  수 μžˆλ‚˜ κ³ λ―Όν•΄ λ΄€μŠ΅λ‹ˆλ‹€.

 

tsconfig.json νŒŒμΌμ„ μˆ˜μ •ν•΄ κ°€λŠ₯ν• κΉŒ μ‚΄νŽ΄λ΄€μ§€λ§Œ, μ»΄νŒŒμΌλŸ¬κ°€ νŠΉμ • ν•¨μˆ˜ λ°˜ν™˜κ°’을 νƒ€μž…κ°€λ“œλ‘œ μΈμ‹ν•˜λ„둝 λ§Œλ“œλŠ” μ„€μ •은 μ‘΄μž¬ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

 

TypeScript compiler(μ΄ν•˜: tsc) μ˜€ν”ˆ μ†ŒμŠ€λ₯Ό μˆ˜μ •ν•˜λ©΄ κ°€λŠ₯ν•  것도 κ°™μ•˜μ§€λ§Œ, 배보닀 배꼽이 더 큰 격이라 λΉ„νš¨μœ¨μ μ΄λΌ μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

 

ν•¨μˆ˜ λ¦¬νŒ©ν† λ§?

μ™ΈλΆ€ μš”μ†Œλ₯Ό μˆ˜μ •ν•˜μ—¬ κ°€λŠ₯ν•œ 점은 λͺ»μ°Ύμ•˜κΈ°μ— λ‚΄μž₯ μœ ν‹Έ ν•¨μˆ˜λ₯Ό λ¦¬νŒ©ν† λ§ν•˜μ—¬ κ°€λŠ₯ν•˜κ²Œ ν•΄λ³΄μž μƒκ°ν–ˆμŠ΅λ‹ˆλ‹€.

 

vIsEmpty ν•¨μˆ˜λŠ” λ°˜ν™˜κ°’μ΄ boolean μž…λ‹ˆλ‹€. λ°˜ν™˜νƒ€μž…μ΄ boolean 이기 λ•Œλ¬Έμ— νƒ€μž…κ°€λ“œκ°€ λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

μ™œλƒν•˜λ©΄ TSλŠ” “boolean을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜”( νƒ€μž… 정보λ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠμŒ) λ₯Ό νƒ€μž…κ°€λ“œλ‘œ μΈμ •ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.
즉, booleanλ§Œμ„ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜λŠ” νƒ€μž… 좔둠에 아무 νžŒνŠΈλ„ μ£Όμ§€ μ•ŠλŠ”λ‹€λŠ” λœ»μž…λ‹ˆλ‹€. 

 

 

κ·Έλž˜μ„œ vIsEmpty ν•¨μˆ˜ κΈ°λŠ₯을 λŸ°νƒ€μž„μ‹œμ μ—μ„œ 보μž₯ν•˜λ©΄μ„œλ„ 컴파일 μ‹œμ μ— boolean 만이 μ•„λ‹Œ νƒ€μž… 정보도 μ œκ³΅ν•  수 μžˆλŠ” λ°©μ•ˆμ΄ μžˆλŠ”μ§€ μ‚΄νŽ΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

 

 

Type predicate

문제 ν•΄κ²° λ°©λ²•은 λ°”λ‘œ Type predicate μž…λ‹ˆλ‹€.

Type predicateλŠ” ν•¨μˆ˜μ˜ λ°˜ν™˜κ°’μ— λ§€κ°œλ³€μˆ˜ is νŠΉμ • νƒ€μž… ν˜•νƒœμ˜ νƒ€μž… 단언(type assertion)을 μ‚¬μš©ν•˜μ—¬,
ν•΄λ‹Ή ν•¨μˆ˜κ°€ νŠΉμ • νƒ€μž…을 λ°˜ν™˜ν•  λ•Œ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ λ³€μˆ˜μ˜ νƒ€μž…을 λ” κ΅¬μ²΄μ μΈ νƒ€μž…μœΌλ‘œ μΆ”λ‘ (type narrowing)ν•˜λ„λ‘ λ•λŠ” μ‚¬μš©μž μ •μ˜ νƒ€μž… κ°€λ“œμž…λ‹ˆλ‹€. (μ‚¬μš©λ²• : parameterName is Type)

μ•„λž˜ μ˜ˆμ‹œκ°€ μžˆμŠ΅λ‹ˆλ‹€.

function isNumber(v: any): v is number {
  return typeof v === "number";
}

 

isNumber ν•¨μˆ˜ κ²°κ³Όκ°€ true일 λ•Œ,
→ TSλŠ” “이 μ‹œμ  μ΄ν›„ vλŠ” number νƒ€μž…”으둜 μ’ν˜€μ„œ μΆ”λ‘ ν•©λ‹ˆλ‹€.

isNumber ν•¨μˆ˜ κ²°κ³Όκ°€ false일 λ•Œ,
→ vλŠ” “numberκ°€ μ•„λ‹Œ νƒ€μž…”으둜 μ’ν˜€μ§‘λ‹ˆλ‹€.

 

TypeScript의 Type predicate (v is Something)λŠ” λŸ°νƒ€μž„μ—μ„œλŠ” boolean, μ»΄νŒŒμΌ νƒ€μž„μ—μ„œλŠ” νƒ€μž… μ •보λ₯Ό μ „λ‹¬ν•©λ‹ˆλ‹€.

Type predicate λŠ” JS λŸ°νƒ€μž„μ—μ„œ boolean을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜μ—¬μ•Ό ν•˜κ³ ,

TypeScript νƒ€μž… μ‹œμŠ€ν…œμ—μ„œ Type predicate λŠ” parameter is Type ν˜•νƒœμ˜ νƒ€μž… 정보λ₯Ό ν•¨κ»˜ μ œκ³΅ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

TypeScript의 νƒ€μž… κ°€λ“œλŠ” “λΆ„κΈ° 쑰건”을 근거둜 νƒ€μž…μ„ μ’νžˆλŠ”(narrowing) μ‹œμŠ€ν…œμž…λ‹ˆλ‹€.

 

 

 

Type predicate κ°œλ…μ„ vIsEmpty ν•¨μˆ˜μ—λ„ μ μš©ν–ˆμŠ΅λ‹ˆλ‹€.

// vIsEmptyκ°€ trueλ©΄ null | undefined 라고 쒁힘
function vIsEmpty<T>(v: T | null | undefined): v is null | undefined;

 

vIsEmpty ν•¨μˆ˜κ°€ trueλ₯Ό λ°˜ν™˜ν•˜λ©΄ vλŠ” null λ˜λŠ” undefined νƒ€μž…μ΄ λ©λ‹ˆλ‹€.

즉, vIsEmpty 결과값이 trueλ©΄ vκ°€ null λ˜λŠ” undefined, falseλ©΄ NonNullableμ΄λΌλŠ” λœ»μž…λ‹ˆλ‹€.


νƒ€μž…κ°€λ“œ ν•¨μˆ˜μ˜ λ°˜ν™˜κ°’ true/falseλŠ” μ»΄νŒŒμΌλŸ¬μ—κ²Œ νƒ€μž… μ •보λ₯Ό μ „λ‹¬ν•˜λŠ” μš©λ„λ‘œ μ‚¬μš©λ  μˆ˜ μžˆμŠ΅λ‹ˆλ‹€.

κ²‰μœΌλ‘œ 보면 vIsEmpty의 λ°˜ν™˜ νƒ€μž…μ΄ v is null | undefinedλΌμ„œ boolean이 μ•„λ‹Œ κ²ƒμ²˜λŸΌ λ³΄μ΄μ§€λ§Œ,

μ‹€μ œλ‘œλŠ” “boolean κ°’ + νƒ€μž…정보 νžŒνŠΈ” λ‘ μ—­ν• μ„ λ™μ‹œμ— ν•©λ‹ˆλ‹€.

 

λ‹€μ‹œ 말해 μœ„ ν•¨μˆ˜λŠ” λŸ°νƒ€μž„ μ‹œμ μ—” true/false,
컴파일 μ‹œμ μ—” "true → v"λŠ” null | undefined,
"false → v"λŠ” NonNullable 둜 ν•΄μ„λ©λ‹ˆλ‹€.


μ •λ¦¬ν•˜μžλ©΄ boolean을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜μ΄μ§€λ§Œ, TS 컴파일러 μž…μž₯μ—μ„œλŠ” “boolean + νƒ€μž… 힌트”κ°€ μ„žμ—¬ μžˆλŠ” μ…ˆμž…λ‹ˆλ‹€.

κ²°κ΅­ vIsEmptyλŠ” boolean을 λ°˜ν™˜ν•˜κ³ , TSλŠ” κ·Έ κ²°κ³Όλ₯Ό μ΄μš©ν•΄ λ³€μˆ˜μ˜ νƒ€μž…μ„ μ’ν˜€μ£ΌλŠ” ν•¨μˆ˜κ°€ 되게 λ©λ‹ˆλ‹€.

 

이런 λ¦¬νŒ©ν† λ§μ„ 톡해 Core λ‚΄μž₯ ν•¨μˆ˜μ˜ κ΅¬ν˜„λΆ€λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³ λ„ μ‹œκ·Έλ‹ˆμ²˜ 변경을 톡해 νƒ€μž… κ°€λ“œκΉŒμ§€ κ°€λŠ₯ν•˜κ²Œ 해쀄 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

 

 

 

πŸ§ͺ검증

ν•¨μˆ˜λͺ…을 κ΅¬λΆ„ν•˜κΈ° μœ„ν•΄ μ˜€λ²„λ‘œλ”© ν•˜μ§€μ•Šκ³  vIsEmpty2둜 λ§Œλ“€μ–΄ ν…ŒμŠ€νŠΈ νŒŒμΌμ„ λ§Œλ“€μ–΄ 검증을 ν•΄λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

// vIsEmpty2κ°€ trueλ©΄ null | undefined 라고 쒁힘
function vIsEmpty2<T>(v: T | null | undefined): v is null | undefined;

 

//isEmpty.spec.ts
(function test_vIsEmpty2() {

    // true cases
    Assert.equal(_.vIsEmpty2("undefined"), true, "_.vIsEmpty2('undefined')");
    Assert.equal(_.vIsEmpty2(undefined), true, "_.vIsEmpty2(undefined)");
    Assert.equal(_.vIsEmpty2(null), true, "_.vIsEmpty2(null)");
    Assert.equal(_.vIsEmpty2(""), true, "_.vIsEmpty2('')");
    Assert.equal(_.vIsEmpty2([]), true, "_.vIsEmpty2([])");
    Assert.equal(_.vIsEmpty2({}), true, "_.vIsEmpty2({})");

    // false cases
    Assert.equal(_.vIsEmpty2("λ‚΄κ°€ 제일 μž˜λ‚˜κ°€"), false, "_.vIsEmpty2('λ‚΄κ°€ 제일 μž˜λ‚˜κ°€')");
    Assert.equal(_.vIsEmpty2([1, 2]), false, "_.vIsEmpty2([1, 2])");
    Assert.equal(_.vIsEmpty2(0), false, "_.vIsEmpty2(0)");
    Assert.equal(_.vIsEmpty2(1.1235), false, "_.vIsEmpty2(1.1235)");
    Assert.equal(_.vIsEmpty2(true), false, "_.vIsEmpty2(true)");
    Assert.equal(_.vIsEmpty2(false), false, "_.vIsEmpty2(false)");

    const test_data = { a: 'abc', b: 1 };
    Assert.equal(_.vIsEmpty2(test_data), false, "_.vIsEmpty2(test_data)");
    Assert.equal(_.vIsEmpty2(test_data.a), false, "_.vIsEmpty2(test_data.a)");
    test_vIsEmpty3(test_data);

    const test_data2 = { a: undefined, b: 1 };
    test_vIsEmpty3(test_data2);

    // νƒ€μž…κ°€λ“œ ν…ŒμŠ€νŠΈ (컴파일 νƒ€μž„ ν™•μΈμš©)
    const maybeValue: string | null = Math.random() > 0.5 ? "HELLO" : null;

    if (!_.vIsEmpty2(maybeValue)) {
        // μ—¬κΈ°μ„œ maybeValueλŠ” NonNullable<string | null> → string 으둜 μ’ν˜€μ§
        const upper = maybeValue.toLowerCase(); // 컴파일 였λ₯˜ μ—†μ–΄μ•Ό 함
        Assert.equal(typeof upper, "string", "type guard narrowing check");
    } else {
        Assert.equal(maybeValue == null, true, "vIsEmpty2(true) -> null or undefined");
    }
    console.log('type guard test end')
})();

//κΈ°μ‘΄ vIsEmpty와 비ꡐ
function test_vIsEmpty3(abc: { a?: string; b: number }) {
    // aλŠ” string λ˜λŠ” undefined
    if (!_.vIsEmpty2(abc.a)) { // vIsEmpty λŠ” 컴파일 였λ₯˜ μƒκΈ°λŠ” κ±Έ 확인함. vIsEmpty2 λ₯Ό λΆ™μ΄λ‹ˆ 컴파일 였λ₯˜ 사라짐. νƒ€μž… κ°€λ“œ μž˜λ˜λŠ” κ±Έ 확인.
        // μ—¬κΈ°μ„œ abc.aλŠ” string으둜 μ’ν˜€μ§
        const upper = abc.a.toUpperCase();
        Assert.equal(typeof upper, "string", "type guard narrowing check");
        Assert.equal(upper, "ABC", "result check");
    } else {
        // μ—¬κΈ°μ„œ abc.aλŠ” undefined둜 μ’ν˜€μ§
        Assert.equal(abc.a === undefined, true, "vIsEmpty2(true) -> undefined");
    }
};

 

 

 

πŸ€”λ§ˆλ¬΄λ¦¬ν•˜λ©°

test case 도 λͺ¨λ‘ ν†΅κ³Όν•˜λ©΄μ„œ vIsEmpty2() ν•¨μˆ˜κ°€ μ‹€μ§ˆμ μœΌλ‘œ λŸ°νƒ€μž„μ‹œμ— boolean 값을 λ°˜ν™˜ν•˜λ©΄μ„œλ„ νƒ€μž… κ°€λ“œκ°€ 잘 μž‘λ™ν•˜λŠ” κ±Έ ν™•μΈν•˜μ˜€μŠ΅λ‹ˆλ‹€.
μΆ”κ°€μ μœΌλ‘œ vIsEmpty() 와 λΉ„μŠ·ν•œ ν•¨μˆ˜ vIsNull() 도 μ μš©ν•  수 μžˆλ‹€κ³  νŒλ‹¨ν•΄ Type predicate 을 μ μš©ν•΄ λ΄€μŠ΅λ‹ˆλ‹€.

 

이번 κ²½ν—˜μ€ λ‹¨μˆœνžˆ “κΉ”λ”ν•œ μ½”λ“œ”λ₯Ό λ§Œλ“œλŠ” 것이 μ•„λ‹ˆλΌ, λŸ°νƒ€μž„ μ•ˆμ •μ„±κ³Ό νƒ€μž… μ‹œμŠ€ν…œμ„ ν†΅ν•©μ μœΌλ‘œ λ°”λΌλ³΄λŠ” 관점을 μ‹¬μ–΄μ€¬μŠ΅λ‹ˆλ‹€.

λ˜ν•œ Core λ‚΄μž₯ μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ“€μ€ 만인이 μ‚¬μš©ν•˜μ§€λ§Œ κ°œμ„ ν•  생각을 ν•˜μ§€ λͺ»ν•˜λŠ” ν•¨μˆ˜λ“€μΈλ°, λ¬Έμ œμ μ„ μΈμ‹ν•˜κ³  이λ₯Ό κ°œμ„ ν•˜λ €κ³  ν–ˆλ‹€λŠ” μŠ€μŠ€λ‘œμ—κ²Œ “쑰금 더 λ‚˜μ€ μ½”λ“œλ₯Ό λ§Œλ“€μ–΄κ°ˆ 수 μžˆλŠ” 개발자”λΌλŠ” 확신을 쀄 수 μžˆμ—ˆλ˜ κ²½ν—˜μ΄μ—ˆμŠ΅λ‹ˆλ‹€.

 

λ¦¬νŒ©ν† λ§μ„ 톡해 λ‚΄μž₯ ν•¨μˆ˜μ˜ νƒ€μž… μ‹œμŠ€ν…œκ³Ό λŸ°νƒ€μž„ μ‚¬μ΄μ˜ 간극을 쀄이고, 사내 λ™λ£Œ 개발자 뢄듀이 더 μ•ˆμ „ν•˜κ³  λͺ…ν™•ν•œ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλŠ” λ°©μ•ˆμ„ μ œκ³΅ν–ˆλ‹€λŠ” μ μ—μ„œ 큰 μ˜λ―Έκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.


μ•žμœΌλ‘œλ„ 기쑴의 λ ˆκ±°μ‹œλ₯Ό λ‹Ήμ—°ν•˜κ²Œ 받아듀이지 μ•Šκ³ , μž‘μ€ λΆˆνŽΈλ„ κ°œμ„ ν•˜λŠ” μ‹œλ„λ₯Ό 계속해 λ‚˜κ°€μ•Όκ² λ‹€κ³  λ‹€μ§ν•˜κ²Œ λμŠ΅λ‹ˆλ‹€.

λŒ“κΈ€