Queue 클래스의 data 프로터티는 타입 선언을 생략 하였기 때문에 any[]타입이 된다. any[]타입은 어떤 타입의 요소도 가질 수 있는 배열을 의미한다. any[]타입은 배열의 요소의 타입이 모두 같지 않다는 문제를 가지게 된다. 위 예제의 경우 data 프로퍼티는 number 타입만을 포함하는 배열이라는 기대 하에 각 요소에 대해 Number.prototype.toFixed를 사용하였다. 따라서 number 타입이 아닌 요소의 경우 런타입 에러가 발생한다. 위와 같은 문제를 해결하기 위해 Queue 클래스를 상속하여 number 타입 전용 NumberQueue 클래스를 정의 해보자
class Queue{ protected data = []; push(item){ this.data.push(item) } pop(){ this.data.shift(); } } //Queue 클래스를 상속하여 number 타입 전용 NumberQueue 클래스 정의 class NumberQueue extends Queue{ //number타입의 요소만을 push한다. push(item:number){ super.push(item); } pop():number{ returnsuper.pop(); } } const queue = new NumberQueue(); queue.push(0); // 의도하지 않은 실수를 사전 검출 가능 // [ts] Argument of type '"1"' is not assignable to parameter of type 'number'. //quueue.push('0') queue.push(+'0'); console.log(queue.pop().toFixed()); // 0
이와 같이 number 타입전용 NumberQueue 클래스를 정의하면 number 타입이외의 요소 추가(push)되었을 때 , 런타임 에러 이전에 에러를 사전에 감지 할 수 있다.
하지만 다양한 타입을 지원해야 한다면 타입 별로 클래스를 상속받아 추가 해야하므로 이 또한 좋은 방법은 아니다.
class Queue<T>{ protected data :Array<T> = []; push(item:T){ this.data.push(item); } pop():T{ returnthis.data.shift()p; } } //number 전용 Queue const numberQueue = new Queue<number>(); numberQueue.push(0); // numberQueue.push('1'); // 의도하지 않은 실수를 사전 검출 가능 numberQueue.push(+'1'); // 실수를 사전 인지하고 수정할 수 있다
제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입이 아닌 다양한 타입을 사용할 수 있도록하는 기법이다. 한 번의 선언으로 다양한 타입에 재사용이 가능하다는 장점이 있다.
T는 제네릭을 선언할 때 관용적으로 사용되는 식별자로 타입 파라미터라 한다. T는 Type의 약자로 반드시 T을 사용해야하는 것은 아니다. 또한 함수에도 제네릭을 사용할 수 있다. 제네릭을 사용하면 하나의 타입이 아닌 다양한 타입의 매개 변수와 리턴 값을 사용할 수 있다.