728x90
반응형

제네릭은 어떠한 클래스 혹은 함수에서 사용할 타입을 그 함수나 클래스를 사용할 때 결정하는 프로그래밍 기법을 말한다.

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>
}
728x90
반응형

'프론트엔드 > TypeScript' 카테고리의 다른 글

TypeScript 설치와 사용  (0) 2022.07.13

+ Recent posts