리엑트에서 이벤트 핸들러의 타입을 정할때는 이벤트의 타입을 정하면 되는데,
리액트에서 제공하는 ChangeEvent,MouseEvent같은 타입을 가지고 제네릭으로는 타겟의 타입을 넣어 주면 된다
function handleChange(e: ChangeEvent<HTMLInputElement>) {
const { name, value } = e.target;
const nextValues = {
...values,
[name]: value,
};
setValues(nextValues);
}
리액트에서 제공하는 ChangeEvent를 사용하고 해당 name,value 타입을 선언하기 위해 제네릭에 HTMLInputElement 타입을 선언한다
function handleClick(e: MouseEvent) {
e.preventDefault();
const message = `${values.username}님 환영합니다`;
alert(message);
}
리액트에서 제공하는 MouseEvent를 사용한다.
이때 handleClick컴포넌트처럼 MouseEvent 타입의 속성을 잘 활용하지 않고 타입을 정리하기 귀찮으면 SyntheticEvent라는 타입을 사용해도 된다
위의 글처럼 만약 해당 컴포넌트 처럼 MouseEvent의 속성을 딱히 활용하고 있지도 않고 타입을 정리하기 귀찮을때는 리엑트에서 제공하는 보편적인 타입인 SyntheticEvent라는 타입을 사용한다
- SyntheticEvent는 리액트 이벤트 객체에 조상격인 타입
- 리액트에서는 브라우저 기본 이벤트를 그대로 쓰는게 아니라 리액트에서 추가적인 기능을 덧붙인 객체를 사용하기 때문에 SyntheticEvent라는 이벤트 객체 타입을 쓴다
- SyntheticEvent는 특별히 이벤트를 구체적으로 지정하지 않아도 괜찮은 경우에는 SyntheticEvent를 쓸 수 있다
interface Props {
className?: string;
id?: string;
children?: ReactNode;
onClick: (e: MouseEvent) => void;
//이벤트 컴포넌트 타입 설정 방법
}
onClick: (e: MouseEvent) => void;
onClick MouseEventHandler<HTMLButtonElement>
주로 1번처럼 다른 함수 타입들과 일관되게 다음과 같은 화살표 함수로 사용한다
함수의 반환 타입: TypeScript에서 함수의 반환 타입이 void로 지정되면, 이 함수가 아무 값도 반환하지 않는다는 의미다. 즉, onChange 함수는 어떠한 값을 반환하지 않으며, 단지 특정 작업을 수행할 뿐이다.
제네릭은 일종의 “타입 매개변수”라고 생각할 수 있다. 일반 함수나 클래스와 달리, 제네릭은 여러 타입에서 동작할 수 있도록 설계한다.
1. 제너릭이 없는 경우
제네릭이 없는 경우, 다양한 타입을 처리하려면 각각의 타입에 대해 별도로 함수를 작성해야 한다.
function identityString(arg: string): string {
return arg;
}
function identityNumber(arg: number): number {
return arg;
}
2. 제너릭이 있는 경우
제네릭을 사용하면 하나의 함수로 다양한 타입을 처리할 수 있다.
function identity<T>(arg: T): T {
return arg;
}
여기서 T는 타입 매개변수이다. identity 함수는 T라는 타입을 받아들이고, 입력받은 값의 타입을 그대로 반환한다.
const resultString = identity<string>("Hello");
const resultNumber = identity<number>(123);
- 타입 안전성: 제네릭을 사용하면, 컴파일 타임에 타입 검사를 통해 오류를 미리 잡을 수 있다.
- 코드 재사용성: 다양한 타입에서 동작하는 코드의 재사용성을 높일 수 있다.
- 유연성: 함수나 클래스가 특정 타입에 종속되지 않으므로, 더 유연한 코드를 작성할 수 있다.
인터페이스는 TypeScript에서 객체의 구조를 정의하는 데 사용되는 일종의 “약속”이라고 할 수 있다. 인터페이스는 특정 객체가 가져야 할 속성(프로퍼티)들과 그 타입을 명확하게 정의해 주며, 이를 통해 코드의 일관성을 유지하고 타입 안전성을 높일 수 있다.
1. 인터페이스 없이 객체를 정의
const person = {
name: "John",
age: 30,
isStudent: false,
};
여기서 person 객체는 name, age, isStudent라는 세 가지 속성을 가지고 있다.
그런데 만약 이 구조를 여러 곳에서 사용해야 한다면, 반복적으로 동일한 속성들을 정의해야 하므로 실수하기 쉽고 코드의 일관성이 떨어질 수 있다.
2. 인터페이스 사용
위와 같은 문제를 해결하기 위해 인터페이스를 사용한다. 인터페이스는 “이런 구조를 가진 객체가 필요해”라는 것을 미리 정의해 주는 것이다.
interface Person {
name: string;
age: number;
isStudent: boolean;
}
위의 Person 인터페이스는 name은 문자열(string), age는 숫자(number), isStudent는 불리언(boolean) 타입이어야 한다고 정의다. 이제 이 인터페이스를 사용하여 객체를 생성하면 된다.
const person: Person = {
name: "John",
age: 30,
isStudent: false,
};
여기서 person 객체는 Person 인터페이스를 따르므로, 속성과 그 타입을 정확하게 맞춰야 한다. 만약 속성 이름을 잘못 작성하거나 타입을 잘못 지정하면, TypeScript는 에러를 발생시켜 잘못된 부분을 알려준다.
결론적으로, 인터페이스는 TypeScript에서 객체의 구조를 명확하게 정의하고, 이를 통해 일관된 코드를 작성하도록 도와주는 도구이다.