전에 있는 포스팅 코드를 가져와보자.
function mergeData<T, U>(a: T, b: U) { return Object.assign(a, b); } const user = mergeData({ name: "Jenny", hobbies: ["sing a song", "play game"] }, 27);
만약 두번째 파라미터가 오브젝트가 아닌 number
여도 리턴값인 Objext.assign
의 계산결과에 따라 우리가 기대하는 값이 안 나왔을 뿐이지, 어떠한 애러도 내지 않는다.
string
이 들어갈 경우 글자수를 쪼개서 반환하고, 배열이 들어갈 경우 index값을 key로 걸어두어 새로운 오브젝트가 생성된다.
function mergeData<T extends object, U extends object>(a: T, b: U) { return Object.assign(a, b); } const user = mergeData({ name: "Jenny", hobbies: ["sing a song", "play game"] }, 24);
변수 타입에도 extends
를 사용해 들어오는 타입을 명확시 시킬 수 있다.
다른 코드의 예를 보자.
function countAndPrint<T>(element: T) { let desc: string; if (element.length === 1) { desc = "one length"; } else if (element.length > 1) { desc = "several length"; } return [element, desc]; }
이런 애러를 반환한다.
- 타입스크립트는
element
가 어떤 타입으로 들어오는지 정확히 모르기 때문에length
프로퍼티가 없다는 애러- 변수가 할당되기 전에 리턴으로 반환되었다는 애러
이렇게 바꿔보자.
interface Lengthy{ length:number } //애러 1 해결 function countAndPrint<T extends Lengthy>(element: T) { let desc: string = ""; // 애러 2해결 if (element.length === 1) { desc = "one length"; } else if (element.length > 1) { desc = "several length"; } return [element, desc]; }
T extends Lengthy라는 제약조건을 걸어, 이로써 얻는 것이 무엇이든 length속성도 반환되며 배열이나 문자열을 length
속성을 지닌다는 것을 알수 있다.
더 구체적으로 정의하기 위해 return값도 튜플 타입으로 타입을 지정해준다.
function countAndPrint<T extends Lengthy>(element: T):[T,string] { let desc: string = ""; if (element.length === 1) { desc = "one length"; } else if (element.length > 1) { desc = "several length"; } return [element, desc]; }
class storage를 만들어 보자.
class StorageItems { private data = []; addItem(item) { this.data.push(item); } removeItem(item) { this.data.splice(this.data.indexOf(item), 1); } getItems() { return [...this.data]; } }
이렇게 코드를 작성하면, item
에 대한 타입이 없으므로 애러를 발생한다.
이때 제네릭을 사용해주면 된다.
class StorageItems<T> { private data:T[] = []; // T 타입의 배열을 입력함으로써 제네릭 타입의 데이터가 저장되도록 한다. addItem(item: T) { this.data.push(item); } removeItem(item : T) { this.data.splice(this.data.indexOf(item), 1); } getItems() { return [...this.data]; } }
생성자를 만들어 보자
const textStorage = new StorageItems(); textStorage.addItem('hello')
이렇게 아무런 타입을 지정해주지 않으면 StorageItems
에 unknown
타입이 지정된다.
◾ unknown 타입
- unknown 타입은 어떤 타입의 값도 할당할 수 있다. Top Type이며, 컴파일러에게 어떤 타입이 들어올 지 모르니 추론해달라고 말하는 타입이다.
any
타입과 다른점은unknown
타입을 지정하면 값을 할당 할 수 없다.
생성자의 타입을 string
으로 지정한다면
const textStorage = new StorageItems<string>(); textStorage.addItem("hello"); const result = textStorage.getItems(); result[0].toFixed() // error
이렇게 사용 할 수 있음.
만약 <T>
의 타입범위를 어느 타입으로만 한정하고 싶다면, extends를 사용해서 타입의 범위를 한정 할 수 있다.
class StorageItems<T extends string|number|boolean> { private data: T[] = []; addItem(item: T) { this.data.push(item); } removeItem(item: T) { this.data.splice(this.data.indexOf(item), 1); } getItems() { return [...this.data]; } }} }