μ¬λ΄ 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 λ΄μ₯ μ νΈλ¦¬ν° ν¨μλ€μ λ§μΈμ΄ μ¬μ©νμ§λ§ κ°μ ν μκ°μ νμ§ λͺ»νλ ν¨μλ€μΈλ°, λ¬Έμ μ μ μΈμνκ³ μ΄λ₯Ό κ°μ νλ €κ³ νλ€λ μ€μ€λ‘μκ² “μ‘°κΈ λ λμ μ½λλ₯Ό λ§λ€μ΄κ° μ μλ κ°λ°μ”λΌλ νμ μ μ€ μ μμλ κ²½νμ΄μμ΅λλ€.
리ν©ν λ§μ ν΅ν΄ λ΄μ₯ ν¨μμ νμ μμ€ν κ³Ό λ°νμ μ¬μ΄μ κ°κ·Ήμ μ€μ΄κ³ , μ¬λ΄ λλ£ κ°λ°μ λΆλ€μ΄ λ μμ νκ³ λͺ νν μ½λλ₯Ό μμ±ν μ μλ λ°©μμ μ 곡νλ€λ μ μμ ν° μλ―Έκ° μμμ΅λλ€.
μμΌλ‘λ κΈ°μ‘΄μ λ κ±°μλ₯Ό λΉμ°νκ² λ°μλ€μ΄μ§ μκ³ , μμ λΆνΈλ κ°μ νλ μλλ₯Ό κ³μν΄ λκ°μΌκ² λ€κ³ λ€μ§νκ² λμ΅λλ€.
'JavaScript Programming' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
| JavaScript μΌλ° ν¨μμ νμ΄ν ν¨μμ μ°¨μ΄μ λ¬Έλ² (0) | 2024.12.20 |
|---|---|
| JavaScript - μ€ν 컨ν μ€νΈμ λ μ컬 νκ²½ (0) | 2024.12.19 |
| JavaScript - if λ¬Έ Truthy μ Falsy νκ° λ°©μ (1) | 2024.12.18 |
| JavaScriptμμ 1 < x < 3μ΄ νμ μ°ΈμΈ μ΄μ - Number λ΄μ₯ ν¨μ (0) | 2024.12.17 |
λκΈ