728x90
제네릭 기본 문법
/*
function logText(text) {
console.log(text)
return text
}
logText(10) //10
logText('하이') //문자열 '하이'
logText(true)//진위값 true
*/
function logText<T>(text: T): T {
console.log(text)
return text
}
logText<string>('하이') //파라미터 타입과 리턴 타입을 실행하는 시점에 넣어준다.
- 일반적으로 타입을 쓰지 않게 되면, 모든 타입을 받을 수 있는 any타입으로 선언된다.
- 제네릭 타입을 사용하게 되면, 런타임 시점에 파라미터 타입과 리턴 타입을 받을 수 있다
기존 타입 정의 방식과 제네릭 차이(함수 중복 선언의 단점)
function logText(text:string){
console.log(text)
//text.split('').reverse().join('') //text가 string 타입이기 때문에 가능한 것이다
return text
}
function logNumber(num: number){
console.log(num)
return num
}
//모든 타입을 받을 수 있다 -> 암묵적으로 logText에서 타입을 받지 않았기 때문
logText('a')
var num = logNumber(10);//이렇게 하면 문제 없이 동작한다. -> 단 유지보수 관점에서 중복되는 코드를 계속 작성해 나가므로 좋지 않다.
- 같은 기능을 제공하는 함수에 타입과, 메서드 명을 다르게 하여 제공하면 여러 타입을 파라미터로 받을 수 있다.
- 단, 이러한 함수 중복은 중복 코드 문제를 야기하며, 유지보수 관점에서도 좋지 않다.
기존 문법과 제네릭 차이(유니온 타입을 이용한 선언 방식의 문제점)
function logText(text:string | number){ //유니온 타입 //함수 하나에서 여러 타입을 받는다
console.log(text)
//text는 string, number을 교집합만, 사용할 수 있다.
return text
}
var a = logText('a'); //a타입은 string | number가 된다
logText(10)
- 유니온 타입을 사용하면, 함수 중복 문제를 해결할 수 있다.
- 단, 유니온 타입을 단점이 있다.
- 유니온 타입으로 온 파라미터는 교집합 API만 제공한다.
- 결과로 반환된 타입 역시 string | number라는 유니온 타입이 된다.
제네릭의 장점과 타입 추론에서의 이점
function logText<T>(text): T { //입력 받을 타입과 리턴타입을 제네릭으로 받는다.
console.log(text)
return text
}
const str = logText<string>('a') //반환되는 타입과 파라미터 모두 string으로 들어간다.
str.split('') //str이라는 값이 string이라는 것을 알 수 있다.
const login = logText<boolean>(true)
- 입력받을 타입과 리턴 타입을 런타임 시점에 받을 수 있다.
- 반환되는 타입이 제네릭이므로 해당 타입을 반환받았을 때에는 그에 상응하는 API를 곧잘 쓸 수 있다.
- 여러 API를 만들어도 중복 없이 만들 수 있다.
인터페이스에 제네릭을 선언하는 방법
//인터페이스에 제네릭을 선언하는 방법
/*
interface Dropdown {
value: string;
selected: boolean;
}
//const obj: Dropdown = {value: 10, selected: false} //오류
*/
interface Dropdown<T> {
value: T;
selected: boolean
}
const objNumber: Dropdown<number> = {value: 10, selected: false} //제네릭 선언
const objString: Dropdown<string> = {value: "abc", selected: false} //제네릭 선언
- 인터페이스에 제네릭을 선언하면 런타임 시점에 value에 타입을 주입할 수 있다.
- 여러 타입을 받는 인터페이스를 중복 선언할 필요가 없다
제네릭의 타입 제한
//제네릭의 타입 제한
function logTextLength<T>(text: T[]): T[]{
console.log(text.length) //text.length가 되지 않는다 why? text에 어떤 값이 들어올지 ts가 알 수 없다.
text.forEach(function (text){
console.log(text)
})
return text
}
logTextLength(['hi', 'abc']) //
- 제네릭에 string에 종속적인 API를 호출하게 되면, TS 가 해당 타입이 어떤 것인지 명확하게 알 수 없기 때문에 사용할 수 없다.
- 이를 해결하기 위해서는 타입 제한을 주어 TS에 힌트를 주어야한다 -> 타입 제한
정의된 타입으로 타입을 제한하기
interface LengthType {
length: number;
}
function logTextLength<T extends LengthType>(text: T): T { //T라는 것은 LengthType을 하위 타입으로 반드시 length가 있다
text.length;
return text
}
logTextLength('a')//a라는 것은 length가 존재하기 때문에 오류가 안난다.
//logTextLength(10) //오류, 숫자 10에는 length가 없기 때문
logTextLength({length: 10})
- extends라는 키워드를 통해 제네릭타입에도 제한을 걸 수 있다.
- 현재는 반드시 length라는 프로퍼티(필드)가 존재해야 한다.
keyOf로 제네릭 타입 제한 하기
//제네릭 타입 제한 3 - keyOf
interface ShoppingItem{
name: string;
price: number;
stock: number;
}
function getShoppingItemOption<T extends keyof ShoppingItem>(itemOption: T):T { //타입에 인터페이스에 존재하는 키속성만 받게 하겠다
return itemOption
}
getShoppingItemOption("name")
- keyOf를 통해 인터페이스에 존재하는 키값으로 제약할 수가 있다.
728x90