정적 타입 언어에서는 특정 타입을 위해 만들어진 함수/클래스를 범용적으로 재사용하기 위해 제네릭이라는 기법을 사용한다.
ex) Stack 자료구조
class Stack<T> {
private data: T[] = [];
constructor() {}
push(item: T): void {
this.data.push(item);
}
pop(): T {
return this.data.pop();
}
}
const numberStack = new Stack<number>();
const stringStack = new Stack<string>();
이런식으로 타입을 지정할 수 있다.
# yarn을 사용하는 경우
yarn create react-app my-app --template typescript
# npx를 사용하는 경우
npx create-react-app my-app --template typescript
yarn add typescript @types/node @types/react @types/react-dom @types/jest
npx tsc --init
yarn tsc --init
{
"compilerOptions": {
"allowJs": true,
"target": "ES5",
"outDir": "./dist",
"moduleResolution": "Node",
"lib": ["ES2015", "DOM", "DOM.Iterable"]
},
"include": ["./src/**/*"],
"exclude": ["node_modules", "dist"]
}
TypeScript: Select a TypeScript Version 검색하여 버전 선택해주기
-> 해결되지 않아서 더 검색
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"esModuleInterop": true,
"noImplicitAny": true,
"strictNullChecks": false,
"jsx": "react",
"sourceMap": true,
"typeRoots": [
"src/@types/types"
],
}
}
라고 되어있어서
"jsx": "react"
만 지정해주니 해당 문제는 해결되었다.
-> 다른 에러가 발생해서 stack overflow를 확인해보니
react-jsx
이렇게 지정해주어야 한다.
tsconfig.json에서 "lib": [ "ES2016", "DOM", "DOM.Iterable" ]
으로 변경
DOM이 없어서 오류가 발생했던 것이다.
TypeScript로 생성된 프로젝트에서 코드를 가져왔다.
reportWebVitals.ts
import { ReportHandler } from 'web-vitals';
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;
index.tsx
root 부분만 변경
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
이제 본격적으로 컴포넌트들에서 타입 지정을 해보자
문서를 참고
https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/basic_type_example
App.tsx
title과 content를 입력하는 코드들에서 event에 type 지정을 해주어야한다.
이벤트 찾기 -> 제네릭으로 전달
const titleAddHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
setTitle(event.target.value);
};
const contentAddHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
setContent(event.target.value);
};
특정 데이터를 불러오는데 데이터의 명확한 타입을 모를 경우에 나타난다.
useState([])이런 형태에 담아 사용할 텐데 다음과 같이 수정하면 된다.
const [working, setworking] = useState<any[]>([]);
working과 관련된 에러가 사라진다.
textarea를 다루는 컴포넌트에서 InputElement 제네릭으로 넘겨주었는데, 에러가 발생하여 찾아보니 TextArea가 따로 있었다.
const newContentHandler = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
setnewContent(event.target.value);
};
HTML DOM API는 다음을 참고하면 된다.
https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API#html_dom_api_interfaces
코드를 수정하고 실행하기 위해 props:any로 전부 수정해주었다. -> 더 명확하게 바꾸기!
type을 모아둬서 파일을 정리할 수 있다.
export type Restaurant = {
name: string;
category: string;
}
이렇게 interface도 extends가 가능하다.
interface 새로운것 extends 미리정의한것 {
추가...
}
수정을 위한 input과 textarea가 다른 컴포넌트에 있기 때문에 ref로 해당 DOM을 지정해준다.
{updateState && (
<div className="update-box" ref={updateBoxRef}>
<UpdateContent item={props.item}></UpdateContent>
</div>
)}
TypeScript에서는 어떤 타입인지 지정해주어야 하기 때문에 그냥 querySelector를 사용하면 안된다.
// 수정 title, content
const findUpdateBoxTitle = (
updateBoxRef.current?.querySelector(".update-title") as HTMLInputElement
)?.value;
const findUpdateBoxContent = (
updateBoxRef.current?.querySelector(
".update-content"
) as HTMLTextAreaElement
)?.value;
typescript에서 type만 지정해주면 될줄 알았는데, DOM요소의 type을 알아야한다. TypeScript를 복습하면서 문서를 읽는 것에 익숙해지고 DOM요소를 더 알아봐야겠다.