typescript 다른 일반 함수, keyof 제약 조건, 제네릭 타입과 유니언 타입의 차이

cptkuk91·2022년 12월 17일
1

TypeScript

목록 보기
13/13
post-thumbnail
interface Lengthy {
	length: number;
}

function countAndPrint<T extends Lengthy>(element: T) {
	let descriptionText = "Got No Value";
    if(element.length === 1){
    	descriptionText = "Got 1 element"
    } else if(element.length > 1){
    	descriptionText = "Got" + element.length + "elements");
    }
    return [element, descriptionText];
}

console.log(countAndPrint("Hello World");
// ["Hello World", "Got 11 elements"]

keyof 제약 조건

key값을 넘겨줄 때 사용된다. 따라서 U extends T를 사용하면 된다.

function extractAndConvert<T extends object, U extends keyof T>(obj: T, key: U){
	return obj[key];
}

// 뒷쪽에 명확한 키값 name을 적어준다. name: "max"에서 key값은 name이다. 뒷쪽에 age라는 잘못된 key값을 넣어주면 에러가 발생한다.
console.log(extractAndConvert({name: "max"}, "name");

제네릭 class

제네릭 class를 사용하는 이유
유연하게 재활용이 가능하다. textStorage 대신 numberStorage를 활용할 때 제네릭 class를 사용할 수 있다.

class DataStorage<T> {
	private data: T[] = [];
    
    addItem(item: T){
    	this.data.push(item);
    }
    
    // 효율적으로 removeItem을 작동시키기 위해서는 조건문을 달아주는 것이 좋다.
    // ex) 제거대상이 없을 때
    removeItem(item: T){
    	// 제거대상이 없는 경우 -1
    	if(this.data.indexOf(item) === -1){
        	return;
        }
    	this.data.splice(this.data.indexOf(item), 1);
    }
    
    getItems(){
    	return [...this.data];
    }
}

const textStorage = new DataStorage<string>();
textStorage.addItem("max");
textStorage.addItem("mana");
textStorage.removeItem("max");
console.log(textStorgage(getItem);
// "mana" 만 출력

const objStorage = new DataStorage<object>();
objStorage.addItem({name: "max"});
objStorage.addItem({name: "mana"});
objStorage.removeItem({name: "mana"});
console.log(objStorage.getItems());
// {name: "max"} 출력

제네릭 유틸리티 타입(Readonly 포함)

종종 사용된다. (typescript에만 존재한다.)

interface Course {
	title: string;
    description: string;
    completeUntil: Date;
}

function createCourseGoal(title: string, description: string, date: Date): Course {
	// return {title: title, description: description, completeUntil: date};
    // Partial을 활용해 하나씩 집어넣을 수 있다. Course에 모든 type 정의된다. 하지만 한 번에 넣기보다 쪼개서 넣고 싶은 경우, Partial<Course>를 활용해 하나씩 활용이 가능하다.
    let result: Partial<Course> = {};
    result.title = title;
    result.description = description;
    result.completeUntil = date;
    // 마지막 반환값은 type을 어디서 가져왔는지 as를 통해 명시해야한다.
    return result as Course;
}

// 잠금 장치!!
const names: Readonly<string[]> = ["max", "sports"];
// 추가를 막고자 할 때 readonly를 사용하면 된다.
// names.push("mana"); 에러를 발생시킨다. Readonly 때문에 막혔다.

제네릭 타입과 유니언 타입의 차이

<T> 냐 (number | string | boolean)[] 이냐..

완전 세분화 할 것이냐, 아니면 느슨하게 허용하면서 type을 체크해줄 것이냐 차이가 있다.

물론 느슨하다고 해서 typescript의 장점을 버리는 것이 아니다.

모든 함수 호출마다 다른 타입을 아주 확실히 지정해야하는 경우에는 유니언 타입 (number | string)[]을 사용하는 것이 맞다.

결론

제네릭 타입을 사용하면 제네릭 함수 등에 사용할 때 구체적인 타입의 범위를 좁힐 수 있다.

다른 여러 가지 가능한 타입과 함께 작동하는 타입을 사용하는 경우 제네릭 타입이 효율적이다.

profile
메일은 매일 확인하고 있습니다. 궁금하신 부분이나 틀린 부분에 대한 지적사항이 있으시다면 언제든 편하게 연락 부탁드려요 :)

0개의 댓글