제네릭은 어떠한 클래스 혹은 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정하는 프로그래밍 기법을 말한다.
Java나 C++등의 정적 타입 언어에서는 함수 및 클래스를 선언하는 시점에서 매개변수 혹은 리턴 타입을 정의해야하기 때문에 기본적으로 특정 타입을 위해 만들어진 클래스나 함수를 다른 타입을 위해 재사용할 수 없다.
그래서 제네릭을 통해 함수와 클래스의 범용적인 사용을 가능하게 한다.
JavaScript는 타입 에러를 런타임에서 일으킨다. 코드를 실행시키기 전까지는 함수와 클래스가 모든 타입에 대응하기 때문에 JavaScript에서는 필요가 없다.
제네릭은 꺽쇠를 넣고 그 안에 타입으로 사용되는 식별자를 집어넣는다. 클래스에서 제네릭을 사용하겠다고 선언한 경우 T는 해당 클래스에서 사용할 수 있는 특정한 타입이 된다.
배열을 입력으로 받아 그 배열의 첫번째 요소를 출력하고 싶은 경우, 제네릭을 쓰는 것과 안 쓰는 경우를 비교해보자.
function first(arr:any[]):any {
return arr[0];
}
위의 코드는 어떤 타입의 배열이라도 받을 수 있기 때문에 리턴하게 되는 타입이 무엇인지 알 수 없다.
function first<T>(arr: T[]):T {
return arr[0];
}
first<number>([1,2,3]); // 1
사용할 때는 함수를 호출할 때 제네릭 문법으로 타입을 정해주기만 하면 된다.
다음과 같은 배열이 주어졌을 때, age가 가장 높은 요소를 반환하는 함수를 구현한다고 해보자.
const ages = [
{ age: 10 },
{ age: 12 },
{ age: 19 },
{ age: 22 },
]
typescript를 사용하지 않는다면 이런 식으로 작성한다.
- code
- function getOldest(ages) { return ages.sort((a, b) => b.age - a.age)[0]; /* const sortedAges = ages.sort((a, b) => b.age - a.age); return sortedAges[0]; */ }
하지만 typescript로는 이렇게 작성해야 한다.
- code
- type ageObj = { age: number } function getOldest(ages: ageObj[]): ageObj { return ages.sort((a, b) => b.age - a.age)[0]; }
자 하지만 만약 객체가 더 복잡한 형태면 어떻게 해야 할까?
예를 들어 ages가 아닌 persons 배열이 주어졌다고 해보자.
const persons = [
{ name: "jinho", age: 22, gender: "male", address: "A"},
{ name: "jinho2", age: 18, gender: "male", address: "B"},
{ name: "jinho3", age: 28, gender: "male", address: "C"},
]
const oldestPerson = getOldest(persons);
이렇게 되면 우리의 oldestPerson 변수는 { name: string, age: number, gender: string, address: string } 의 타입이 아닌 ageObj 타입을 가지게 된다. 따라서 아래의 동작은 오류를 발생시킨다.
console.log(oldestPerson.name)
// type error: Property 'name' does not exist on type 'ageObj'.
여기서 우리는 getOldest 함수가 조금 더 타입에 자유롭도록 하기 위해 generic을 사용해야 한다.
function getOldest<T extends ageObj>(ages: T[]): T {
return ages.sort((a, b) => b.age - a.age)[0];
}
const persons = [
{ name: "jinho", age: 22, gender: "male", address: "A"},
{ name: "jinho2", age: 18, gender: "male", address: "B"},
{ name: "jinho3", age: 28, gender: "male", address: "C"},
]
interface IPerson {
name: string;
age: number;
gender: string;
address: string;
}
const oldestPerson = getOldest<IPerson>(persons);
import React from 'react'
const App = (): JSX.Element => {
const [count, setCount] = React.useState(0);
const addCount = () => setCount(count + 1);
const minusCount = () => setCount(count - 1);
return <div>{count}</div>
}
import React from 'react'
interface IPerson {
name: string;
age: number;
gender: string;
address: string;
}
const App = (): JSX.Element => {
const [person, setPerson] = React.useState<IPerson>({
name: "",
age: 0,
gender: "",
address: "",
});
React.useEffect(() => {
// data fetching -> setPerson(불러온 데이터)
}, [])
return <div>{person.name}</div>
}
'프론트엔드 > TypeScript' 카테고리의 다른 글
TypeScript 설치와 사용 (0) | 2022.07.13 |
---|