class C{ a:number; b?:number; } let c = new C(); c.a = 12; c.a = undefined; //오류 undefined는 'number'에 할당 할 수 없다. c.b = 13; c.b = undefined//ok; c.b = null//오류 'null'은 'number | undefined'에 할당 할 수 없다.
타입 가드와 타입 단언
Nullable 타입은 유니온 타입으로 구현 되기 때문에 타입가드를 사용하여 null을 제거해야합니다.
컴파일러가 null 또는 undefined를 제거할 수 업슨 경우에 타입 단언 연산자를 사용하여 수동으로 제거해야합니다. 연산자는 ! 입니다. v!은 v!의 null과 undefined을 제거합니다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
functionbroken(name:string | null):string{ //namedl null일 수도 있다는 에러가 발생 functionpostfix(epithet:string){ return name.chartAt(0)+'. the '+ epithet; //오류 'name'이 null일 수 있다. } //name이 null이어도 여기서 "Bob"으로 바뀝니다. name = name || 'Bob'; return prostfix("graet")'' }
functionfixed(name:string | null):string{ functionpostfix(epithet:string){ return name!.charAt(0)+ '. the '+ epithet; } name = name || "Bob"; return postfix("great"); }
컴파일러가 name이 null 일수 있다고 판단하는 이유는 , 외부 함수에서 호출한 경우 중첩된 함수에 대한 모든 호출을 추적하는 것이 불가능하기 때문입니다. (즉시 실행함수 IIFE의 경우 가능)
1 2 3 4 5 6 7
functionbroken(name:string | null):string{ name = name || "Bob"; return(functionpostfix(epithet:string){ //즉시 실행 함수이기 때문에 name이 null이 아니라는 것을 알고 있다. return name.chatAt(0)+ '. the' + epithet; })("great"); }
타입 단언( type Assertion)
타입 스크립트가 컴파일러 타입을 실제 런타입에 존재 할 변수의 타입과 다르게 추론하거나 너무 보수적이 ㄴ추론을 하는 경우에 프로그래머가 수동으로 컴파일러한테 특정 변수에 대해 타입 힌트를 주는것이다.
class Character{ hp:number; runAway(){ } isWizard(){ } isWarrior(){ } } class Wizrd extends Character{ fireBall(){ } } class Warrior extends Character{ attack(){} } functionbattle(character: Character) { if (character.isWizard()) { character.fireBall(); // Property 'fireBall' does not exist on type 'Character'. } elseif (character.isWarrior()) { character.attack(); // Property 'attack' does not exist on type 'Character'. } else { character.runAway(); } }
다음 코드는 컴파일 에러가 발생한다. Character 클래스에는 fireBall ,attack케소드가 선언되어 있지 ㅇ낳기 때문 isWizard라는 메소드를 통해 그 캐릭터가 Wizard인스턴스라는 것을 보장 할수 있다면 FireBall 메소드를 사용할 수있다.
1 2 3 4 5 6 7 8 9
functionbattle(character:Character){ if(character.isWizard()){ (character as Wizard).fireBll(); //pass; }elseif(character.isWarrior()){ (character as Warrior).attact(); //pass }else{ character.runAway(); } }
타입 단언은 크게 두가지 와 as Type 두가지 방법이 존재하는데
1 2
(<Wizard>character).fireBall(); (character as Wizard)/fireBall();
보통 키워드가 좀더 깔끔해 보이지만 React에서 jsx 문법을 사용할 때 와 문법이 겹칠 수 잇기 때문에 불편한 면이 존재한다.
타입가드는 타입으 ㄹ좀더 갈끔하게 할 수 잇도록 도와 준다. 앞서 타이 ㅂ단언에서 소개한 예제에서는 isWizard 라는 메소드로 해당 인스턴스가 해당 타입이라는 사실을 확정했다. 하지만 이건 런타임에서만 알 수 있는 사실이고 TypeScript 컴파일러는 알 수 없었다. 타입 가드는 이러한 런타임에서의 타입 체크를 컴파일러에게 알려주는 기능이다.
class Character{ isWizard():this is Wiazrd{ returnthisinstanceof Wizard; } isWarrior():this is Warrior{ returnthisinstanceof Warrior; } }
functionbattle(character :Character){ if(character.isWizard()){ character.fireBall(); //ok }elseif(character.isWarrior()){ character.attack() //ok; }else{ character.runAway(); } } //이제 별도의 타입 단언 문법없이도 if 블록안에서 character가 Wizard나 Warrior로 자 ㄹ추론이 된다. 그리고 사실 instanceof와 typeof같은 오퍼레이터로 일종의 타입 가드이다
1 2 3 4 5 6 7 8 9
functiondoSomething(val:string | number){ if(typeof val === 'number'){ val.toFixed(); //pass val은 number로 타입 추론 }else{ //union타입에서 'number'는 이미 통과 했으므로 자동으로 'string'으로 추론됨 val.toLowerCase(); //pass, val은 string 타입으로 추론됨 } }