정말 현재 가장 많이 쓰는 React는 javascript 태생이기에 당연히 Typescript의 타입을 쓸 수 있다.
공식문서에서 타입스크립트 추가부분을 본다면
npx create-react-app my-app --template typescript
라고 시작하는 부분을 확인할 수 있다. 여기서 --template typescript
는 옵션 또는 플래그가 Create React 앱에게 Typescript 앱을 만든다고 알려주고 TypeScript를 포함하고 자동으로 tsconfig파일을 생성하도록 한다
이러면 .jsx
파일 대신 .tsx
파일을 생성한다
꼭 CRA를 사용할 필요는 없고 간단하다면 npm install @types/react
를 실행해서 Typescript 타입을 추가해줘도 된다.
함수형 컴포넌트로 만들 예정이다
//Greeter.tsx
import React from "react"
function Greeter() {
return <h1>Hello!</h1>
}
export default Greeter
이렇게 기본적인 컴포넌트를 하나 만들었는데 구문에 타입 애너테이션도 제네릭도 없고 그냥 일반적으로 작성된 것과 같아보인다
하지만 여기서도 타입이 생성된다. 함수의 추론된 반환 타입을 보면 JSX.Element
이다
react 타입 정의나 react-dom 타입 정의를 사용할 때 얻는 타입 정의이다
CRA를 설치할 때 같이 설치된 것으로 Go to Type Definition으로 따라가면 정의를 볼 수 있다
실수를 방지하기 위해 애너테이션을 추가할 수 있는데
//Greeter.tsx
import React from "react"
function Greeter():JSX.Element {
return <h1>Hello!</h1>
}
export default Greeter
이처럼 직접 애너테이션을 추가해주면 된다
그럼 React.FC라고 있던데 이건 뭘까?
React.FC = Function Component의 줄임말로 이는 React 타입의 일부이다. 이전 React강의에서 typescript를 함께 섞어 배울 때 React.FC를 이용했지만 사용하지 않는 게 좋은 것 같아 사용하지 않았었다(실제로 사용하지 않는게 좋다는 글도 많았고 말이다)
사용방법은
//Greeter.tsx
import React from "react"
const Greeter:React.FC = () => {
return <h1>Hello!</h1>
}
export default Greeter
위에 만들었던 Greeter
컴포넌트를 통해 각기 다른 값을 전달해보려고 한다
// App.tsx
function App(){
return (
<div className="App">
<Greeter person="Colt" />
<Greeter person="Blue" />
</div>
)
}
그러면 일단 person
쪽에서 에러가 발생하는데 intrinsicAttributes
타입을 할당할 수 없고 person
프로퍼티는 intrinsicAttributes
타입에 존재하지 않는다고 한다
Greeter
컴포넌트가 person
을 파라미터로 받게 해주면 된다
import React from "react"
function Greeter(props:{person:string}):JSX.Element {
return <h1>Hello!,{props.person}</h1>
}
export default Greeter
이러면 서로 연결시켰다. 만약 받아야하는 데이터 값이 많아질 경우 어떻게 할까?
import React from "react"
interface GreeterProps = {
person: string
}
function Greeter({person}:GreeterProps):JSX.Element {
return <h1>Hello!,{person}</h1>
}
export default Greeter
구조분해 할당을 통해 {person}
으로 분해햇고 interface
를 만들어 해당 타입을 바로 애너테이션으로 집어 넣었다
이렇게 간단히도 가능하다
useState를 사용할 때 타입은 어떻게 사용해야 할까?
타입을 확인해보면 function useState<S>(initalState:S|(()=>S):[S,Dispatch<SetStateAction<S>>]
길게 타입이 적혀 있는 부분을 확인 할 수 잇는데
일단 initalState
를 제공하자. 어차피 우리가 썼던것처럼 해보면
const [item, setItem] = useState([])
이렇게 적는데 문제가 있다.
item
을 확인해보면 items: neverp[]
라고 타입이 생겼다.
[]
를 작성했기 때문에 기본적으로 빈 배열 리터럴 타입으로 설정되는데 우리가 원하는 형태는 아니다
interface Item {
id: number;
product: string;
quantity: number;
}
const [item, setItem] = useState<Item[]>([])
이런식으로 객체 interface
로 타입을 만들고 useState
뒤에 홑화살 구문으로 제네릭 타입을 넣어주면 빈 배열값을 가지지만 item
은 해당 제네릭 배열의 타입을 갖도록 만들 수 있다
cheatsheet를 살펴보면
useRef도 useState처럼 제네릭 타입을 가지고 있기에 타입을 전달해야한다
요소(Element)타입만 인수로 제공하며 초기값으로 null을 사용하기도 한다라 나와있다
const inputRef = useRef<HTMLInputElement>(null);
console.log(inputRef.current?.value);
이처럼 만들면 된다
inputRef.current?.value
여기서 ?
를 사용했는데 null
일수도 있으니 사용한 것으로 !
를 사용해서 null
이 아니라고 확신시켜도 가능하다
react에서 작성시 중요한 차이점으로
1. 코드를 tsx파일에 작성한다
2. 코드 작성시 모든 걸 타입 지정해준다. 특히 프로퍼티를 다룰 때(함수마다 어떤 시그니처와 어떤 프로퍼티 타입이 전달되는지 작성)
보통 interface
를 많이 사용하고 타입 별칭도 물론 쓰지만 같은 파일이나 인라인에서만 사용하는 게 일반적이다.