다른 타입과 연결되는 종류이며 다른 타입이 어떤 타입이어야 하는지에 대해서는 크게 상관하지 않는다.
제네릭 타입을 사용하는 경우 입력되는 데이터 타입을 지정해주어 타입을 결정할 수 있다. 예를 들어 배열은 그 자체로 타입이지만 배열에 전달되어야 하는 데이터 타입을 지정할 수 있다.
const names: Array<string> = []; // string[]과 같고, 데이터가 string 타입 이라는 의미
함수의 매개변수와 반환 타입에 제네릭 타입 T, U를 사용하여 타입스크립트가 입력되는 데이터 타입을 추론하여 타입을 함수 호출을 위해 지정한다.
function merge(objA: object, objB: object) {
return Object.assign(objA, objB);
}
console.log(merge({name: 'Max'}, {age: 30});
// {name: 'Max', age: 30}
//T, U를 입력하여 타입스크립트가 타입을 추론하게 한다. 어떠한 타입이 입력될지 모른다는 의미를 전달.
function merge<T, U>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj = merge({name: 'Max'}, {age: 30})
console.log(mergedObj.age)
extends를 작성하여 타입에 대한 특정 제약 조건을 설정할 수 있다. 이는 즉 원하는 타입을 지정해줄 수 있다.
function merge<T extends object, U extends object>(objA: T, objB: U) {
return Object.assign(objA, objB);
}
const mergedObj = merge({name: 'Max'}, {age: 30})
console.log(mergedObj.name)
interface Lengthy {
length: number;
}
function countAndDescribe<T extends Lengthy>(element: T): [T, string] {
let descriptionText = 'Got no value.';
if(element.length === 1) descriptionText = 'Got 1 element.';
else if(element.length > 1) dedcriptionText = 'Got' + element.length + 'elements.';
return [element, descriptionText];
}
length를 명확하게 명시하기 위해 인터페이스를 사용, T extends Lengthy라는 제약조건을 설정하여 무엇이든 length 속성이 반환되며 배열이나 문자열은 length 속성을 지닌다는 것을 알려준다.
function extractAndConvert(obj: object, key: string) {
return obj[key];
}
입력한 객체가 무엇이든 이 키를 가지는지 알 수 없기 때문에
function extractAndConvert<T extends object, U extends keyof T>(obj: T,key: U) {
return 'Value: ' + obj[key];
}
extractAndConvert({ name: 'Max' }, 'name');
extends keyof T 키워드를 사용하여 정확한 구조를 갖고자 한다는 것을 타입스크립트에 알려준다.
만일 함수의 매개변수 key가 반드시 매개변수 obj의 제네릭 타입 T(객체를 받게되는)에 존재하여야 할때, keyof T 를 하면 객체의 key 값을 모아 유니온 타입으로 만들 수 있다.
typeof
객체 형태의 타입을 따로 속성들만 뽑아 모아 유니온 타입으로 만들어주는 연산자
타입스크립트에 추가적인 정보를 입력하여 하나 이상의 제네렉 타입을 입력할 수 있다. 또한 클래스 내에 자체 제네릭 유형을 지닌 메소드를 입력해도 된다.
class DataStorage<T> {
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];
}
}
const textStorage = new DataStorage<string>();
textStorage.addItem('Max');
textStorage.addItem('Manu');
textStorage.removeItem('Max');
console.log(textStorage.getItems()); //['Manu']
타입을 Optional 타입으로 바꿔주는 타입, 특정 타입의 부분 집합을 만족하는 타입을 정의할 수 있다.
interface MyUser { //기존 코드
name: string;
id: string;
email?: string;
phone?: string;
}
interface MyUser { //partial을 적용하여 모든 요소를 옵셔널 타입으로 생성
name: string;
id: string;
email?: string;
phone?: string;
}
type MyUserOptionals = Partial<MyUser>
위와같이 설정하면 모든 요소를 옵셔널로 지정한 타입으로 생성할 수 있다. 즉 전달받은 타입의 모든 하위 집합을 나타내는 타입을 생성할수 있다.
속성을 변경하거나 이 객체에 새 속성을 추가할 수 없는 읽기만 가능한 타입
const names: Readonly<string[]> = ['Max', 'Anna'];
names.push('Manu'); // 추가는 불가.
유니언 타입은 함수를 호출할 때마다 타입들 중 하나로 호출할 수 있는 함수가 필요한 경우에 유용하다. 즉 모든 메서드 호출이나 모든 함수 호출마다 다른 타입을 지정하고자 함에 사용하자.
class DataStorage {
private data: (string | number | boolean)[] = [];
addItem(item: string | number | boolean) {
this.data.push(item);
}
}
제네릭 타입은 특정 타입을 고정하거나 전체 클래스 인스턴스에 걸쳐 같은 함수를 사용하거나, 전체 함수에 걸쳐 같은 타입을 사용하고자 할때 유용하다. 즉 타입을 고정하여 해당하는 타입만이 필요할때 사용하자.
class DataStorage<T> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
}