https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-kr
https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-kr
Last synced: 4 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-kr
- Owner: typescript-cheatsheets
- License: mit
- Created: 2022-08-23T19:28:07.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2022-10-14T19:05:07.000Z (over 2 years ago)
- Last Synced: 2025-02-10T19:39:54.115Z (4 months ago)
- Size: 73.2 KB
- Stars: 7
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# React+TypeScript Cheatsheets ํ๊ตญ์ดํ ๐ฐ๐ท
TypeScript์ ์ ๋ฌธํ๋ React ๊ฐ๋ฐ์๋ฅผ ์ํ ์นํธ์ํธ(Cheetsheets)
---
[**์น ๋คํ๋จผํธ**](https://react-typescript-cheatsheet.netlify.app/docs/basic/setup) |
[**์์ดํ**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) |
[ํ๋ก์ ํธ์ ๊ธฐ์ฌํ๊ธฐ]() |
[์ง๋ฌธํ๊ธฐ]():wave: ๋ณธ ๋ฆฌํฌ์งํ ๋ฆฌ๋ [@ryan_kim_kr](https://twitter.com/ryan_kim_kr)์ ์ํด ๊ด๋ฆฌ๋๊ณ ์์ต๋๋ค. ๊ฐ๋ฐ์๋์ด React์ ํจ๊ป TypeScript๋ฅผ ์ฌ์ฉํด๋ณด๊ณ ์ ํ์๋ค๋ ์ ๋ง ๊ธฐ์ ์์์ด๊ตฐ์! ์๋ชป๋ ๋ถ๋ถ์ด ๋ฐ๊ฒฌ๋์ด ์์ ์ด ํ์ํ๊ฑฐ๋ ๋๋ฝ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ๊ฐ์ ๋์ด์ผ ํ ์ฌํญ์ [์ด์ ๋ฑ๋ก](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-kr/issues/new)ํด ์ฃผ์๊ธฐ ๋ฐ๋๋๋ค. :+1:
---
[](/COLABORADORES.md)
# All React + TypeScript Cheatsheets
- [๊ธฐ์ด ์นํธ์ํธ(The Basic Cheatsheet)]()๋ React ๊ฐ๋ฐ์๊ฐ React app์์ TS ์ฌ์ฉ์ ์์ํ๋ ๊ฒ์ ๋์์ ์ฃผ๊ธฐ ์ํ ๋ด์ฉ์ด ์ฃผ๋ฅผ ์ด๋ฃน๋๋ค.
- ๋ชจ๋ฒ ์ฌ๋ก(Best Practices)๋ผ๊ณ ์ฌ๊ฒจ์ง๋, ๋ณต์ฌ + ๋ถ์ฌ๋ฃ๊ธฐ ๊ฐ๋ฅํ ์์
- ๊ธฐ๋ณธ์ ์ธ TS Types ์ฌ์ฉ๋ฒ๊ณผ ์ค์ ๋ฐฉ๋ฒ
- ์์ฃผ ๋ฌป๋ ์ง๋ฌธ(FAQ)์ ๋ํ ๋ต๋ณ
- Generic type logic์ ๊น์ด ๋ค๋ฃจ์ง ์์ต๋๋ค. ๊ทธ ๋์ , ์ด์ฌ์๋ค์ ์ํด ๊ฐ๋จํ ํธ๋ฌ๋ธ์ํ ๊ธฐ์ ๋ค์ ์๊ฐํฉ๋๋ค.
- ๊ธฐ์ด ์นํธ์ํธ๋ ๊ฐ๋ฐ์๊ฐ TypeScript์ ๋ํด ๋๋ฌด ๋ง์ ๊ณต๋ถ๋ฅผ ํ์ง ์๊ณ ์๋ **์๊ฐ ํจ์จ์ **์ผ๋ก React ๊ฐ๋ฐ์ TypeScript๋ฅผ ๋น ๋ฅด๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ๋๋ ๋ฐ ๊ทธ ๋ชฉ์ ์ด ์์ต๋๋ค.
- [๊ณ ๊ธ ์นํธ์ํธ(The Advanced Cheatsheet)]()๋ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ type utilities/functions/render prop/higher order copmonents ๋๋ TS+React ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์ ํ๋ ๊ฐ๋ฐ์๋ฅผ ์ํด generic types์ ๊ณ ๊ธ ์ฌ์ฉ๋ฒ์ ๋ํ ์ดํด๋ฅผ ๋์ต๋๋ค.
- ์ ๋ฌธ์ ์ธ ๊ฐ๋ฐ์๋ค์ ์ํ ๋ค์ํ ํ๊ณผ ์๋ น๋ค์ ์๊ฐํฉ๋๋ค.
- DefinitelyTyped์ ๊ธฐ์ฌํ๊ธฐ ์ํ ์กฐ์ธ์ ๋๋ฆฝ๋๋ค.
- ๊ณ ๊ธ ์นํธ์ํธ๋ ๊ฐ๋ฐ์๊ฐ TypeScript๋ฅผ ์ต๋ํ ํ์ฉํ ์ ์๋๋ก ๋๋ ๋ฐ ๊ทธ ๋ชฉ์ ์ด ์์ต๋๋ค.
- [๋ง์ด๊ทธ๋ ์ดํ ์นํธ์ํธ(The Migrating Cheatsheet)]()๋ ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค๋ฅผ JS ๋๋ Flow์์ TypsScript๋ก ์ ์ง์ ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋ ๊ฒ์ ๋ํ ๊ฒฝํ์์ ์กฐ์ธ์ ์ป๋๋ฐ ๋์์ ์ค๋๋ค.
- ์ฐ๋ฆฌ๋ ์ฌ๋ฌ๋ถ์ด ๋ง์ด๊ทธ๋ ์ด์ ์ ํ๋๋ก ์ค๋ํ๋ ค๋ ๊ฒ์ด ์๋๋ฉฐ, ์ด๋ฏธ ๊ทธ๋ ๊ฒ ํ๊ณ ์ ๊ฒฐ์ ํ ์ฌ๋๋ค์ ๋๊ณ ์ ํฉ๋๋ค.
- โ ๏ธ ์ด ์นํธ์ํธ๋ ์๋กญ๊ฒ ๋ง๋ค์ด์ง ์นํธ์ํธ ์ ๋๋ค. ๋ฐ๋ผ์ ๋์์ ์ฃผ๊ณ ์ ํ๋ ๋ชจ๋ ๋ถ๋ค์ ํ์ํฉ๋๋ค.
- [HOC ์นํธ์ํธ(The HOC Cheatsheet)]()๋ ์์์ ํจ๊ป HOC๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ์๋ ค์ค๋๋ค.
- [Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html)์ ๋ํ ์ดํด๊ฐ ์ ํ๋์ด์ผ ํฉ๋๋ค.
- โ ๏ธ ์ด ์นํธ์ํธ๋ ์๋กญ๊ฒ ๋ง๋ค์ด์ง ์นํธ์ํธ ์ ๋๋ค. ๋ฐ๋ผ์ ๋์์ ์ฃผ๊ณ ์ ํ๋ ๋ชจ๋ ๋ถ๋ค์ ํ์ํฉ๋๋ค.---
## ๊ธฐ์ด ์นํธ์ํธ (Basic Cheatsheet)
### ๊ธฐ์ด ์นํธ์ํธ ๋ชฉ์ฐจ
๋ชฉ์ฐจ ํ์ฅํ๊ธฐ
- [์น์ 1: React์ TypeScript ์ค์ ํ๊ธฐ]()
- [์ ์ ์กฐ๊ฑด]()### ์น์ 1: React์ TypeScript ์ค์ ํ๊ธฐ
#### ์์ํ๊ธฐ ์ ํ์ํ ์ฌํญ
1. [React](https://reactjs.org)์ ๋ํ ์ถฉ๋ถํ ์ดํด
2. [TypeScript Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html)์ฃผ์ ์ ๋ํ ์ดํด ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html)๋ฅผ ์๊ณ ์์ผ๋ฉด ๋ฌธ์๋ฅผ ์ดํดํ๋๋ฐ ๋์์ด ๋ฉ๋๋ค. ๋ง์ฝ TypeScript๋ฅผ ์ฒ์ ์ ํ๋ ๋ถ์ด๋ผ๋ฉด,[chibicodeโs tutorial](https://ts.chibicode.com/todo/)๋ฅผ ์ฐธ๊ณ ํด ๋ณด์ธ์.)
3. [the TypeScript section in the official React docs](https://reactjs.org/docs/static-type-checking.html#typescript) ์ฝ๊ธฐ
4. [the React section of the new TypeScript playground](http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react) ์ฝ๊ธฐ (์ ํ์ฌํญ: [the playground's](http://www.typescriptlang.org/play/index.html) Example Section์ 40+ examples ๋จ๊ณ๋ฅผ ์ํํด ๋ณด๊ธฐ)์ด ๊ฐ์ด๋๋ ๋ ์๊ฐ ๊ฐ์ฅ ์ต์ ๋ฒ์ ์ TypeScript์ React๋ฅผ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค. ์ด์ ๋ฒ์ ์ ๋ํ ์ฌํญ์ ํ์ฅ ๊ฐ๋ฅํ `` ํ๊ทธ๋ก ํ์ธ ๊ฐ๋ฅํฉ๋๋ค.
#### VS Code ํ์ฅ ํ๋ก๊ทธ๋จ(Extensions)
- ๋ฆฌํํ ๋ง ๋ณด์กฐ https://marketplace.visualstudio.com/items?itemName=paulshen.paul-typescript-toolkit
- R+TS Code Snippets (์ฌ๋ฌ๊ฐ์ง ํ์ฅ ํ๋ก๊ทธ๋จ์ด ์์ต๋๋ค...)
- https://marketplace.visualstudio.com/items?itemName=infeng.vscode-react-typescript
- https://www.digitalocean.com/community/tutorials/the-best-react-extension-for-vs-code
- TypeScript ๊ณต์ ํ์ฅํ๋ก๊ทธ๋จ https://code.visualstudio.com/docs/languages/typescript#### React + TypeScript ์ ๋ฌธ์ ํคํธ
Cloud setups:
- [TypeScript Playground with React](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcA5FDvmQNwCwAUKJLHAN5wCuqWAyjMhhYANFx4BRAgSz44AXzhES5Snhi1GjLAA8W8XBAB2qeAGEInQ0KjjtycABsscALxwAFAEpXAPnaM4OANjeABtA0sYUR4Yc0iAXVcxPgEhdwAGT3oGAOTJaXx3L19-BkDAgBMIXE4QLCsAOhhgGCckgAMATQsgh2BcAGssCrgAEjYIqwVmutR27MC5LM0yuEoYTihDD1zAgB4K4AA3H13yvbAfbs5e-qGRiYspuBmsVD2Aekuz-YAjThgMCMcCMpj6gxcbGKLj8MTiVnck3gAGo4ABGTxyU6rcrlMF3OB1H5wT7-QFGbG4z6HE65ZYMOSMIA)๋ ์ฝ๋๋ฅผ ์คํํ์ง ์๊ณ Types๋ฅผ ๋๋ฒ๊น ๋ง ํ๋ ๊ฒฝ์ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- [CodeSandbox](http://ts.react.new) - cloud IDE, ๋งค์ฐ ๋น ๋ฅธ ๋ถํ ์๋๋ฅผ ๊ฐ์ง๋๋ค.
- [Stackblitz](https://stackblitz.com/edit/react-typescript-base) - cloud IDE, ๋งค์ฐ ๋น ๋ฅธ ๋ถํ ์๋๋ฅผ ๊ฐ์ง๋๋ค.Local dev setups:
- [Next.js](https://nextjs.org/docs/basic-features/typescript): `npx create-next-app -e with-typescript` ๋ช ๋ น์ด๋ ์๋ก์ด NextJS ํ๋ก์ ํธ๋ฅผ ํ์ฌ ํด๋์ ์์ฑํฉ๋๋ค.
- [Create React App](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app name-of-app --template typescript` ๋ช ๋ น์ด๋ ์๋ก์ด NextJS ํ๋ก์ ํธ๋ฅผ ์๋ก์ด ํด๋์ ์์ฑํฉ๋๋ค.
- [Vite](https://vitejs.dev/): `npm create vite@latest my-react-ts-app -- --template react-ts`
- [Meteor](https://guide.meteor.com/build-tool.html#typescript): `meteor create --typescript name-of-my-new-typescript-app`
- [Ignite](https://github.com/infinitered/ignite#use-ignite-andross-infinite-red-andross-boilerplate) for React Native: `ignite new myapp`
- [TSDX](https://tsdx.io/): `npx tsdx create mylib` ๋ช ๋ น์ด๋ React+TS _๋ผ์ด๋ธ๋ฌ๋ฆฌ_ ๋ฅผ ์์ฑํฉ๋๋ค. (in future: [TurboRepo](https://twitter.com/jaredpalmer/status/1346217789942591488))๋ค๋ฅธ ๋๊ตฌ๋ค
์์ง ๋ณด์์ด ํ์ํ์ง๋ง ํ์ธํด ๋ณผ ๋งํ ๊ฐ์น๊ฐ ์๋ ๋๊ตฌ๋ค:
- [Snowpack](): `npx create-snowpack-app my-app --template app-template-react-typescript`
- [Docusaurus v2](https://v2.docusaurus.io/docs/installation) with [TypeScript Support](https://v2.docusaurus.io/docs/typescript-support)
- [Parcel](https://v2.parceljs.org/languages/typescript/)
- [JP Morgan's `modular`](https://github.com/jpmorganchase/modular): CRA + TS + Yarn Workspaces toolkit. `yarn create modular-react-app `Manual setup:
- [Basarat's guide](https://github.com/basarat/typescript-react/tree/master/01%20bootstrap)๋ React + TypeScript + Webpack + Babel ์ **์๋์ผ๋ก ์ค์ ** ํ ๊ฒฝ์ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ํนํ, `@types/react`์ `@types/react-dom`๊ฐ ์ค์น๋์ด ์๋์ง ํ์ธ์ด ํ์ํฉ๋๋ค. ([์ต์ํ์ง ์์ ๋ด์ฉ์ด๋ผ๋ฉด DefinitelyTyped project ์ ๋ํด ๋ ์์๋ณด์ธ์.](https://definitelytyped.org/))
- ๋ํ ๋ง์ React + TypeScript bolierplates๋ค์ด ์์ต๋๋ค. [์ฐ๋ฆฌ์ ๋ค๋ฅธ ๋ฆฌ์์ค ๋ฆฌ์คํธ](https://react-typescript-cheatsheet.netlify.app/docs/basic/recommended/resources/)๋ฅผ ํ์ธํด์ฃผ์ธ์.#### ๋น๋์ค ํํ ๋ฆฌ์ผ
์๋์ 7๋ถ๋ก ๊ตฌ์ฑ๋ "React Typescript Course" ๋น๋์ค ์๋ฆฌ์ฆ๋ฅผ ํตํด TypeScript with React์ ๋ํ ์๊ฐ๋ฅผ ๋ค์ ์ ์์ต๋๋ค.
### Section 2: ์์ํ๊ธฐ
#### ํจ์ ์ปดํฌ๋ํธ
ํจ์ ์ปดํฌ๋ํธ๋ `props`๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๊ณ JSX element๋ฅผ ๋ฐํํ๋ ์ผ๋ฐ์ ์ธ ํจ์๋ก ์์ฑ๋ ์ ์์ต๋๋ค.
```tsx
// props์ ํ์ ์ ์ - ๋ ๋ง์ ์์๋ "์ปดํฌ๋ํธ Props ํ์ดํ"์์ ํ์ธํ ์ ์์ต๋๋ค.
type AppProps = {
message: string;
}; /* export ํ๋ค๋ฉด consumer๊ฐ extendํ ์ ์๋๋ก `interface`๋ฅผ ์ฌ์ฉํ์ธ์. */// ํจ์ ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ ์ ์๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ; return type์ ์ถ๋ก ๋ฉ๋๋ค.
const App = ({ message }: AppProps) =>{message};// ์ค์๋ก ๋ค๋ฅธ ํ์ ์ ๋ฐํํ์์ ๋ ์๋ฌ๊ฐ raise ๋๋๋ก return type์ ๋ช ์ํ ์ ์์ต๋๋ค.
const App = ({ message }: AppProps): JSX.Element =>{message};// type ์ ์ธ์ ํจ์ ์ปดํฌ๋ํธ ์ ์ธ์ ํฌํจ์ํฌ ์ ์์ต๋๋ค.;์ด ๋ฐฉ๋ฒ์ prop types์ ์ด๋ฆ์ ๋ถ์ด์ง ์์๋ ๋์ง๋ง ์ฝ๋๊ฐ ๋ฐ๋ณต๋ฉ๋๋ค.
const App = ({ message }: { message: string }) =>{message};
```> Tip: type destructure ์ ์ธ์ ์ํด [Paul Shen's VS Code Extension](https://marketplace.visualstudio.com/items?itemName=paulshen.paul-typescript-toolkit)๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค. ([keyboard shortcut](https://twitter.com/_paulshen/status/1392915279466745857?s=20)์ ์ถ๊ฐ ํ์ธ์.)
React.FC
๊ฐ ๊ถ์ฅ๋์ง ์๋ ์ด์ ๋ ๋ฌด์์ผ๊น์?React.FunctionComponent
/React.VoidFunctionComponent
๋ ์ด๋ค๊ฐ์?React+TypeScript codebases์์ ๋ค์ ๋ณด์์ ์ ์์ต๋๋ค.
```tsx
const App: React.FunctionComponent<{ message: string }> = ({ message }) =>{message};
```ํ์ง๋ง, ํ์ฌ `React.FunctionComponent` (๋๋ ๊ฐ๋ตํ๊ฒ ์จ์ `React.FC`)๋ [๊ถ์ฅ๋์ง ์๋๋ค๋ ๊ฒ](https://github.com/facebook/create-react-app/pull/8177)์ ๋๋ถ๋ถ์ ์ฌ๋๋ค์ด ๋์ํฉ๋๋ค. ๋ฌผ๋ก ์ด ์ฃผ์ ์ ๋ํ ๋ฏธ๋ฌํ ์๊ฒฌ ์ฐจ์ด๊ฐ ์์ ์๋ ์์ง๋ง, ๋ง์ฝ ์ด ์๊ฒฌ์ ๋์ํ๊ณ `React.FC`๋ฅผ ๋น์ ์ ์ฝ๋๋ฒ ์ด์ค์์ ์ ๊ฑฐํ๊ณ ์ถ๋ค๋ฉด, [์ด jscodeshift codemond](https://github.com/gndelia/codemod-replace-react-fc-typescript)๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
"์ผ๋ฐ์ ์ธ ํจ์" ๋ฒ์ ๊ณผ์ ์ฐจ์ด์ ๋ค์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- `React.FunctionComponent`๋ return type์ ๋ช ์์ ์ผ๋ก ๋ฐํ๋๋ค. ํ์ง๋ง ์ผ๋ฐ์ ์ธ ํจ์ ๋ฒ์ ์ ์์์ ์ ๋๋ค(๋๋ ์ถ๊ฐ์ ์ธ ์ด๋ ธํ ์ด์ (annotation)์ด ํ์ํฉ๋๋ค).
- `displayName`, `propTypes`, ๊ทธ๋ฆฌ๊ณ `defaultProps`์ ๊ฐ์ static properties๋ฅผ ์ํ ์๋์์ฑ(autocomplete)๊ณผ ํ์ ์ฒดํฌ(Typechecking)๋ฅผ ์ง์ํฉ๋๋ค.
- `React.FunctionComponent`์ ํจ๊ป `defaultProps`์ ์ฌ์ฉํ๋๋ฐ ๋ช ๊ฐ์ง ์๋ ค์ง ๋ฌธ์ ๊ฐ ์์ต๋๋ค. [๋ฌธ์ ์ ๋ํ ์์ธํ ๋ด์ฉ](https://github.com/typescript-cheatsheets/react/issues/87)์ ํ์ธํ์ธ์. ์ฐ๋ฆฌ๋ ๊ฐ๋ฐ์๋์ด ์ฐพ์๋ณผ ์ ์๋ ๋ณ๊ฐ์ `defaultProps` ์น์ ์ ์ ๊ณตํ๊ณ ์์ต๋๋ค.
- [React 18 type ์ ๋ฐ์ดํธ](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210) ์ด์ ์๋, `React.FunctionComponent`์ด `children`์ ๋ํ ์์์ ์ธ ์ ์(implicit definition)๋ฅผ ์ ๊ณตํ์์ต๋๋ค. ์ด๊ฒ์ ์ด๋ค ํ ๋ก ๊ณผ์ ์ ๊ฑฐ์ณค๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก [`React.FC`๊ฐ Create React App TypeScript template์์ ์ ๊ฑฐ](https://github.com/facebook/create-react-app/pull/8177)๋ ์ด์ ์ค ํ๋๊ฐ ๋์์ต๋๋ค.
```tsx
// React 18 types ์ด์
const Title: React.FunctionComponent<{ title: string }> = ({ children, title }) => (
{children}
);
```(Deprecated)
React.VoidFunctionComponent
๋๋React.VFC
์ฌ์ฉํ๊ธฐ[@types/react 16.9.48](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643)์์, `React.VoidFunctionComponent` ๋๋ `React.VFC` type์ `children`์ ๋ช ์์ ์ผ๋ก ํ์ดํ(typing) ํ๊ธฐ ์ํด ์ถ๊ฐ๋์์ต๋๋ค.
ํ์ง๋ง, `React.VFC`์ `React.VoidFunctionComponent`๋ React 18 (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59882) ์์ ๋์ด์ ์ฌ์ฉ๋์ง ์๊ฒ ๋์์ต๋๋ค(deprecated). ๋ฐ๋ผ์ ์ด ์์๋ฐฉํธ์ React 18+ ์์ ๋์ด์ ๊ถ์ฅ๋์ง ์์ต๋๋ค.์ผ๋ฐ์ ์ธ ํจ์ ์ปดํฌ๋ํธ๋ `React.FC`๋ฅผ ์ฌ์ฉํด ์ฃผ์ธ์.
```ts
type Props = { foo: string };// ์ง๊ธ์ ๊ด์ฐฎ์ง๋ง, ๋ฏธ๋์๋ ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ๊ฒ์ ๋๋ค.
const FunctionComponent: React.FunctionComponent = ({ foo, children }: Props) => {
return (
{foo} {children}
); // OK
};// ์ง๊ธ์ ์๋ฌ๋ฅผ ๋ฐ์์ํค๊ณ , ๋ฏธ๋์๋ ๋์ด์ ์ฌ์ฉ๋์ง ์์๊ฒ์ ๋๋ค.(Deprecated)
const VoidFunctionComponent: React.VoidFunctionComponent = ({ foo, children }) => {
return (
{foo}
{children}
);
};
```- _๋ฏธ๋์๋_, props๋ฅผ ์๋์ผ๋ก `readonly` ๋ผ๊ณ ํ์ํ ์๋ ์์ต๋๋ค. ํ์ง๋ง, props ๊ฐ์ฒด๊ฐ ํ๋ผ๋ฏธํฐ ๋ฆฌ์คํธ์์ destructure ๋๋ค๋ฉด, ์ด๊ฒ์ ์๋ฏธ์๋ ํ๋ ์ ๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ ์ด๋ค syntax๋ฅผ ์ฌ์ฉํ๋์ง ํฐ ์ฐจ์ด๊ฐ ์์ง๋ง, `React.FunctionComponent`์ ๋ณด๋ค ๋ช ์์ ์ธ ํน์ฑ์ ์ ํธํ๋ ๊ฒ์ด ์ข์๊ฒ์ ๋๋ค.
์ฃผ์ํด์ผ ํ ์ฌํญ
๋ค์์ ํจํด์ ์ง์๋์ง ์์ต๋๋ค. :
**์กฐ๊ฑด๋ถ ๋ ๋๋ง(conditional rendering)**
```tsx
: false); // JS ์์๋ ์ด๋ ๊ฒ ํ์ง ๋ง์ญ์์ค.
const MyConditionalComponent = ({ shouldRender = false }) => (shouldRender ?
const el = ; // ์๋ฌ๋ฅผ throw ํฉ๋๋ค.
```์ด ํจํด์ด ์ง์๋์ง ์๋ ์ด์ ๋ ์ปดํ์ผ๋ฌ์ ํ๊ณ ๋๋ฌธ์ ๋๋ค. ํจ์ ์ปดํฌ๋ํธ๋ JSX expression ๋๋ `null` ์ด์ธ์ ๋ค๋ฅธ ์ด๋ค ๊ฒ๋ ๋ฐํํ ์ ์์ต๋๋ค. ๋ฐํํ ์ ์๋ ๊ฒ์ด ๋ฐํ๋๋ค๋ฉด ํด๋น ํ์ ์ `Element`์ ํ ๋น๋ ์ ์๋ค๋ ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ๋ณด๊ฒ๋ ๊ฒ์ ๋๋ค. ("{the other type} is not assignable to `Element`.")
**Array.fill**
```tsx
);
const MyArrayComponent = () => Array(5).fill(
const el2 = ; // throws an error
```์์ฝ๊ฒ๋ ํจ์์ ํ์ ์ annotate ํ๋ ๊ฒ์ ์๋ฌด๋ฐ ๋์์ด ๋์ง ์์๊ฒ์ ๋๋ค. React๊ฐ ์ง์ํ๋ ๋ค๋ฅธ ํน๋ณํ ํ์ (exotic type)์ ๋ฐํํ๊ณ ์ ํ๋ค๋ฉด ํ์ ํ๋ช (type assertion)์ ์ํํด์ผ ํฉ๋๋ค. :
```tsx
) as any as JSX.Element;
const MyArrayComponent = () => Array(5).fill(
```[์ฌ๊ธฐ์ @ferdaber ์ ์ค๋ช ์ ํ์ธํด๋ณด์ธ์](https://github.com/typescript-cheatsheets/react/issues/57).
#### Hooks
Hook์ [`@types/react` v16.8 ์ด์๋ถํฐ ์ง์๋ฉ๋๋ค](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031).
#### useState
ํ์ ์ถ๋ก (Type inference)์ ๊ฐ๋จํ ๊ฐ๋ค์ ์ ์๋ํฉ๋๋ค:
```tsx
const [state, setState] = useState(false);
// `state` ๋ boolean ์ผ๋ก ์ถ๋ก ๋ฉ๋๋ค.
// `setState` ๋ boolean ๊ฐ ๋ง์ ๋ฐ์ต๋๋ค.
```ํ์ ์ถ๋ก ์ ๋ณต์กํ ํ์ ์ ์ฌ์ฉํด์ผ ํ๋ค๋ฉด [์ถ๋ก ๋ ํ์ (Inferred Types) ์ฌ์ฉํ๊ธฐ](https://react-typescript-cheatsheet.netlify.app/docs/basic/troubleshooting/types/#using-inferred-types) ๋ ํ์ธํด๋ณด์ธ์.
ํ์ง๋ง ๋ง์ hook ๋ค์ null ๊ฐ์ ๊ฐ๋ฅผ ๋ํดํธ ๊ฐ์ผ๋ก ์ด๊ธฐํ ํ๊ธฐ ๋๋ฌธ์ ์ด๋ป๊ฒ ํ์ ์ ์ง์ ํ๋์ง ๊ถ๊ธํ ์ ์์ต๋๋ค. ๋ช ์์ ์ผ๋ก ํ์ ์ ์ ์ธํ๊ณ , union type์ ์ฌ์ฉํ์ธ์.:
```tsx
const [user, setUser] = useState(null);// later...
setUser(newUser);
```๋ง์ฝ useState์ค์ ์งํ์ state๊ฐ ์ด๊ธฐํ๋๊ณ ๊ทธ ์ดํ์ ํญ์ ๊ฐ์ ๊ฐ์ง๋ค๋ฉด, ํ์ ํ๋ช (type assertions)์ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
```tsx
const [user, setUser] = useState({} as User);// later...
setUser(newUser);
```์ด ๋ฐฉ๋ฒ์ ์ผ์์ ์ผ๋ก ํ์ ์คํฌ๋ฆฝํธ ์ปดํ์ผ๋ฌ์๊ฒ `{}`๊ฐ `User`์ type์ด๋ผ๊ณ "๊ฑฐ์ง๋ง" ํฉ๋๋ค. ๊ทธ ํ์ `user` state๋ฅผ ์ค์ ํ์ฌ์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด ๋๋จธ์ง ์ฝ๋๊ฐ `user`๋ `User` ํ์ ์ด๋ผ๋ ์ฌ์ค์ ์์กด๊ณ ์ด๊ฒ์ ๋ฐํ์ ์๋ฌ๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
#### useReducer
Reducer actions๋ฅผ ์ํด [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions)๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. reducer์ return type์ ์ ์ํ๋ ๊ฒ์ ์์ง ๋ง์ธ์. ๊ทธ๋ ์ง ์์ผ๋ฉด ํ์ ์คํฌ๋ฆฝํธ๊ฐ return type์ ์ถ๋ก ํ ๊ฒ์ ๋๋ค.
```tsx
import { useReducer } from "react";const initialState = { count: 0 };
type ACTIONTYPE = { type: "increment"; payload: number } | { type: "decrement"; payload: string };
function reducer(state: typeof initialState, action: ACTIONTYPE) {
switch (action.type) {
case "increment":
return { count: state.count + action.payload };
case "decrement":
return { count: state.count - Number(action.payload) };
default:
throw new Error();
}
}function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
dispatch({ type: "decrement", payload: "5" })}>-
dispatch({ type: "increment", payload: 5 })}>+
>
);
}
```[TypeScript Playground์์ ๋ณด๊ธฐ](https://www.typescriptlang.org/play?#code/LAKFEsFsAcHsCcAuACAVMghgZ2QJQKYYDGKAZvLJMgOTyEnUDcooRsAdliuO+IuBgA2AZUQZE+ZAF5kAbzYBXdogBcyAAwBfZmBCIAntEkBBAMIAVAJIB5AHLmAmgAUAotOShkyAD5zkBozVqHiI6SHxlagAaZGgMfUFYDAATNXYFSAAjfHhNDxAvX1l-Q3wg5PxQ-HDImLiEpNTkLngeAHM8ll1SJRJwDmQ6ZIUiHIAKLnEykqNYUmQePgERMQkY4n4ONTMrO0dXAEo5T2aAdz4iAAtkMY3+9gA6APwj2ROvImxJYPYqmsRqCp3l5BvhEAp4Ow5IplGpJhIHjCUABqTB9DgPeqJFLaYGfLDfCp-CIAoEFEFeOjgyHQ2BKVTNVb4RF05TIAC0yFsGWy8Fu6MeWMaB1x5K8FVIGAUglUwK8iEuFFOyHY+GVLngFD5Bx0Xk0oH13V6myhplZEm1x3JbE4KAA2vD8DFkuAsHFEFcALruAgbB4KAkEYajPlDEY5GKLfhCURTHUnKkQqFjYEAHgAfHLkGb6WpZI6WfTDRSvKnMgpEIgBhxTIJwEQANZSWRjI5SdPIF1u8RXMayZ7lSphEnRWLxbFNagAVmomhF6fZqYA9OXKxxM2KQWWK1WoTW643m63pB2u+7e-3SkEQsPamOGik1FO55p08jl6vdxuKcvv8h4yAmhAA)
Redux
์์Reducer
์ ํจ๊ป ์ฌ์ฉํ๊ธฐReducer funciton์ ์์ฑํ๊ธฐ ์ํด [redux](https://github.com/reduxjs/redux)๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, return type์ ์ฒ๋ฆฌํ๋ `Reducer` ํ์์ ํธ๋ฆฌํ helper๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์์ reducer example์ ๋ค์๊ณผ ๊ฐ์ด ๋ฐ๋ ์ ์์ต๋๋ค. :
```tsx
import { Reducer } from 'redux';export function reducer: Reducer() {}
```#### useEffect / useLayoutEffect
`userEffect`์ `userLayoutEffect` ๋ ๋ค side effect๋ฅผ ์ํํ๊ธฐ ์ํด ์ฌ์ฉ๋๊ณ ์ ํ์ ์ผ๋ก cleanup function์ ๋ฐํํฉ๋๋ค. ์ด๊ฒ์ ๋ง์ฝ ์ด hook๋ค์ด ๋ฐํ ๊ฐ์ ์ฒ๋ฆฌํ์ง ์๋๋ค๋ฉด, type์ด ํ์ ์๋ค๋ ๋ป์ ๋๋ค. `useEffect`๋ฅผ ์ฌ์ฉํ ๋, ํจ์ ๋๋ `undefined` ์ด์ธ์ ๋ค๋ฅธ ๊ฒ์ ๋ฐํํ์ง ์๋๋ก ์ฃผ์ํ์ธ์. ๊ทธ๋ ์ง ์์ผ๋ฉด TypeScript์ React๋ ๋น์ ์๊ฒ ๋น๋ช ์ ์ง๋ฅผ๊ฒ์ ๋๋ค. Arros functions๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ด ๋ฌธ์ ๋ ๋ค์ ํ์ ํ๊ธฐ ์ด๋ ค์ธ ์ ์์ต๋๋ค. :
```ts
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;useEffect(
() =>
setTimeout(() => {
/* do stuff */
}, timerMs),
[timerMs]
);
// ๋์ ์์! setTimeout์ ์๋ฌต์ ์ผ๋ก ์ซ์๋ฅผ ๋ฐํํ๊ณ ์์ต๋๋ค.
// arrow function์ body๊ฐ ์ค๊ดํธ๋ก ๊ฐ์ธ์ง์ง ์์๊ธฐ ๋๋ฌธ์ ๋๋ค.
return null;
}
```์ ์์์ ๋ํ ํด๊ฒฐ์ฑ
```tsx
function DelayedEffect(props: { timerMs: number }) {
const { timerMs } = props;useEffect(() => {
setTimeout(() => {
/* do stuff */
}, timerMs);
}, [timerMs]);
// ๋ ๋์ ๋ฐฉ๋ฒ; ํ์คํ๊ฒ undefined๋ฅผ ๋ฐํํ๊ธฐ ์ํด์ void keyword๋ฅผ ์ฌ์ฉํ์ธ์.
return null;
}
```#### useRef
TypeScript์์ `useRef`๋ type argument๊ฐ ์ด๊ธฐ ๊ฐ์ ์์ ํ ํฌํจ(cover)ํ๋์ง ์๋์ง์ ๋ฐ๋ผ[read-only](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1025-L1039)๋๋ [mutable](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1012-L1023) ๋ ์ค ํ๋๋ฅผ ๋ฐํํฉ๋๋ค. ๊ฐ์์ use case์ ๋ง๋ ๊ฒ์ ์ ํํ์ธ์.
##### Option 1: DOM element ref
**[DOM element์ ์ ๊ทผํ๊ธฐ ์ํด์๋](https://reactjs.org/docs/refs-and-the-dom.html):** element type ๋ง์ argument๋ก ๋๊ฒจ์ฃผ๊ณ `null`์ ์ด๊ธฐ ๊ฐ์ผ๋ก ์ฌ์ฉํ์ธ์. ์ด ๊ฒฝ์ฐ์, ๋ฐํ๋๋ reference๋ React์ ์ํด ๊ด๋ฆฌ๋๋ read-only `.current`๋ฅผ ๊ฐ์ง ๊ฒ์ ๋๋ค. TypeScript๋ ์ด ref๋ฅผ element์ `ref` prop์ผ๋ก ์ ๋ฌ ๋ฐ๊ธฐ๋ฅผ ๊ธฐ๋ํฉ๋๋ค. :
```tsx
function Foo() {
// - ๊ฐ๋ฅํ ์์ธํ๊ฒ ์์ฑํ์ธ์. ์๋ฅผ๋ค๋ฉด, HTMLDivElement๋ HTMLElement๋ณด๋ค ๋ ์ข๊ณ ,
// Element๋ณด๋ค๋ ํจ์ ๋ ์ข์ ์ ํ์ ๋๋ค.
// - ๊ธฐ์ ์ ์ผ๋ก ๋งํ์๋ฉด, ์ด๊ฒ์ RefObject๋ฅผ ๋ฐํํฉ๋๋ค.
const divRef = useRef(null);useEffect(() => {
// ref.current๊ฐ null์ผ ์ ์๋ค๋ ๊ฒ์ ์ฃผ์ํ์ธ์.
// ์ด๊ฒ์ ๋น์ ์ด ์กฐ๊ฑด์ ๋ฐ๋ผ์ ref๋(ref-ed) element๋ฅผ renderํ๊ฑฐ๋
// ํ ๋นํ๋ ๊ฒ์ ์์ ์ ์๊ธฐ ๋๋ฌธ์ ์์ธกํ ์ ์๋ ํ์์ ๋๋ค.
if (!divRef.current) throw Error("divRef is not assigned");// ์ด์ divRef.current๋ ํ์คํ๊ฒ HTMLDivElement ์ ๋๋ค.
doSomethingWith(divRef.current);
});// React๊ฐ ๋น์ ์ ์ํด ref๋ฅผ ๊ด๋ฆฌํ ์์๋๋ก element์๊ฒ ref๋ฅผ ์ ๋ฌํด ์ฃผ์ธ์.
returnetc;
}
```๋ง์ฝ `divRef.current`๊ฐ ์ ๋๋ก null์ด ์๋๊ฒ์ด๋ผ๋ ๊ฒ์ ํ์ ํ๋ค๋ฉด, non-null assertion operator `!`์ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ฐ๋ฅํฉ๋๋ค. :
```tsx
const divRef = useRef(null!);
// ๋์ค์... ์ด๊ฒ์ด null ์ธ์ง ํ์ธํ ํ์๊ฐ ์์ต๋๋ค.
doSomethingWith(divRef.current);
```๋น์ ์ด type safety๊ฐ ๋ณด์ฅ๋๋ค๊ณ ๋ฏธ๋ฆฌ ๊ฐ์ ํ๊ณ ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ ๊ฒ์ ์ฃผ์ํด์ผ ํฉ๋๋ค. ๋ ๋๋ง ๊ณผ์ ์์ ref๋ฅผ element์ ํ ๋นํ๋ ๊ฒ์ ์๊ฑฐ๋, ref๋(ref-ed) element๊ฐ ์กฐ๊ฑด๋ถ ๋ ๋๋ง ๋๋ค๋ฉด runtime error๊ฐ ๋ฐ์ํ ๊ฒ์ ๋๋ค.
Tip: ์ด๋ค
HTMLElement
๋ฅผ ์ฌ์ฉํ ์ง ์ ํํ๊ธฐ
Ref๋ ๋ช ์์ฑ(specificity)์ ํ์๋ก ํฉ๋๋ค. ์ฆ, `HTMLElement` ๋ง์ ๋ช ์ํ๋ ๊ฒ์ ์ถฉ๋ถํ์ง ์๋ค๋ ๋ง์ ๋๋ค. ๋ง์ฝ ๋น์ ์ด ํ์ํ element type์ ์ด๋ฆ์ ๋ชจ๋ฅธ๋ค๋ฉด, [lib.dom.ts](https://github.com/microsoft/TypeScript/blob/v3.9.5/lib/lib.dom.d.ts#L19224-L19343)์์ ํ์ธํ๊ฑฐ๋ ์๋์ ์ผ๋ก type error๋ฅผ ๋ฐ์์ํค๊ณ language service๊ฐ type์ ์ด๋ฆ์ ์๋ ค์ฃผ๋๋ก ํ ์ ์์ต๋๋ค.
##### Option 2: Mutable value ref
**[mutable value๋ฅผ ๊ฐ์ง๊ธฐ ์ํด์๋](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables):** ์ํ๋ type์ ์ฌ์ฉํ๊ณ ์ด๊ธฐ ๊ฐ์ด ์์ ํ ํด๋น type์ ์ํ๋์ง ํ์ธํ์ธ์.
```tsx
function Foo() {
// ๊ธฐ์ ์ ์ผ๋ก ๋งํ์๋ฉด, ์ด๊ฒ์ MutableRefObject์ ๋ฐํํฉ๋๋ค.
const intervalRef = useRef(null);// ๋น์ ์ด ์ง์ ref๋ฅผ ๊ด๋ฆฌํฉ๋๋ค. (์ด๊ฒ์ด MutableRefObject๋ผ๊ณ ๋ถ๋ฆฌ๋ ์ด์ ์ด์ฃ .)
useEffect(() => {
intervalRef.current = setInterval(...);
return () => clearInterval(intervalRef.current);
}, []);// ref๋ element์ "ref" prop์ผ๋ก ์ ๋ฌ๋์ง ์์ต๋๋ค.
return Cancel timer;
}
```##### ๋ค์์ ์๋ฃ๋ ํ์ธํด ๋ณด์ธ์.
- [@rajivpunjabi๊ฐ ์์ฑํ ๊ด๋ จ ์ด์](https://github.com/typescript-cheatsheets/react/issues/388) - [Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwCwAUI7hAHarwCCYYcAvHAAUASn4A+OAG9GjOHAD0CBLLnKGcxHABiwKBzgQwMYGxS4WUACbBWAczgwIcSxFwBXEFlYxkxtgDoVTQBJVmBjZAAbOAA3KLcsOAB3YEjogCNE1jc0-zgAGQBPG3tHOAAVQrAsAGVcKGAjOHTCuDdUErhWNgBabLSUVFQsWBNWA2qoX2hA9VU4AGFKXyx0AFk3H3TIxOwCOAB5dIArLHwgpHcoSm84MGJJmFbgdG74ZcsDVkjC2Y01f7yFQsdjvLAEACM-EwVBg-naWD2AB4ABLlNb5GpgZCsACiO083jEgn6kQAhMJ6HMQfpKJCFpE2IkBNg8HCEci0RisTj8VhCTBiaSKVSVIoAaoLnBQuFgFFYvFEikBpkujkMps4FgAB7VfCdLmY7F4gleOFwAByEHg7U63VYfXVg2Go1MhhG0ygf3mAHVUtF6jgYLtwUdTvguta4Bstjs9mGznCpVcbvB7u7YM90B8vj9vYgLkDqWxaeCAEzQ1n4eHDTnoo2801EknqykyObii5SmpnNifA5GMZmCzWOwOJwudwC3xjKUyiLROKRBLJf3NLJO9KanV64xj0koVifQ08k38s1Sv0DJZBxIx5DbRGhk6J5Nua5mu4PEZPOAvSNgsgnxsHmXZzIgRZyDSYIEAAzJWsI1k+BCovWp58gKcAAD5qmkQqtqKHbyCexoYRecw7IQugcAs76ptCdIQv4KZmoRcjyMRaGkU28A4aSKiUXAwwgpYtEfrcAh0mWzF0ax7bsZx3Lceetx8eqAlYPAMAABa6KJskSXAdKwTJ4kwGxCjyKy-bfK05SrDA8mWVagHAbZeScOY0CjqUE6uOgqDaRAOSfKqOYgb8KiMaZ9GSeCEIMkyMVyUwRHWYc7nSvAgUQEk6AjMQXpReWyWGdFLHeBZHEuTCQEZT8xVwaV8BxZCzUWZQMDvuMghBHASJVnCWhTLYApiH1chIqgxpGeCfCSIxAC+Yj3o+8YvvgSLyNNOLjeBGhTTNdLzVJy3reGMBbTtrB7RoB3XbNBAneCsHLatcbPhdV3GrdB1WYhw3IKNZq-W2DCLYRO7QPAljgsgORcDwVJAA)
- [Stefan Baumgartner์ ์์](https://fettblog.eu/typescript-react/hooks/#useref) - [Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA)#### useImperativeHandle
ํด๋น [Stackoverflow answer](https://stackoverflow.com/a/69292925/5415299)์ ๋ฐ๋ฅด๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.:
```tsx
// Countdown.tsx// forwardRef๋ก ์ ๋ฌ๋ handle type์ ์ ์ํฉ๋๋ค
export type CountdownHandle = {
start: () => void;
};type CountdownProps = {};
const Countdown = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
// start() ๋ ์ฌ๊ธฐ์ ํ์ ์ถ๋ก (type inference) ๋ฉ๋๋ค
start() {
alert("Start");
},
}));return
Countdown;
});
``````tsx
// ์ด ์ปดํฌ๋ํธ๋ Countdown ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํฉ๋๋คimport Countdown, { CountdownHandle } from "./Countdown.tsx";
function App() {
const countdownEl = useRef(null);useEffect(() => {
if (countdownEl.current) {
// start()๋ ์ฌ๊ธฐ์๋ ํ์ ์ถ๋ก (type inference) ๋ฉ๋๋ค.
countdownEl.current.start();
}
}, []);return ;
}
```##### ๋ค์์ ์๋ฃ๋ ํ์ธํด๋ณด์ธ์:
- [ForwardRefRenderFunction ์ฌ์ฉํ๊ธฐ](https://stackoverflow.com/a/62258685/5415299)
#### Custom Hooks
๋ง์ฝ Custom Hook์์ array๋ฅผ returnํ๋ค๋ฉด, array์ ๊ฐ ์์น์์ ๊ฐ๊ธฐ ๋ค๋ฅธ type์ ๊ฐ์ง๊ธฐ๋ฅผ ์ํ๊ฒ ์ง๋ง TypeScript๋ union type์ผ๋ก ์ถ๋ก ํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ํ์ ์ถ๋ก ์ ํผํ๊ณ ์ถ์ ๊ฒ์ ๋๋ค. ์ด๋ฌํ ์ํฉ์์ [TS 3.4 const assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions)์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
```tsx
import { useState } from "react";export function useLoading() {
const [isLoading, setState] = useState(false);
const load = (aPromise: Promise) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as const; // (boolean | typeof load)[]์ด ์๋ [boolean, typeof load]์ผ๋ก ์ถ๋ก ํฉ๋๋ค.
}
```[TypeScript Playground์์ ํ์ธํด ๋ณด๊ธฐ](https://www.typescriptlang.org/play/?target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA)
์ด๋ฐ ๋ฐฉ๋ฒ์ผ๋ก, destructureํ์ ๋ destructuregํ ์์น์ ๋ฐ๋ผ ์ฌ๋ฐ๋ฅธ type์ ์ป์ ์ ์์ต๋๋ค.
๋์: tuple return type์ ํ๋ช ํ๊ธฐ(assert)
๋ง์ฝ [const assertions์ด ์ฌ์ฉํ๊ธฐ ์ด๋ ต๋ค๋ฉด](https://github.com/babel/babel/issues/9800), ํจ์ return type์ ํ๋ช (assert)ํ๊ฑฐ๋ ์ ์ํ ์ ์์ต๋๋ค.
```tsx
import { useState } from "react";export function useLoading() {
const [isLoading, setState] = useState(false);
const load = (aPromise: Promise) => {
setState(true);
return aPromise.finally(() => setState(false));
};
return [isLoading, load] as [boolean, (aPromise: Promise) => Promise];
}
```๋ง์ custom hooks์ ์์ฑํ๋ค๋ฉด, ์๋์ผ๋ก tuples์ ํ์ ์ ๋ช ์ํด์ฃผ๋ helper๋ ํฐ ๋์์ด ๋ ์ ์์ต๋๋ค.
```tsx
function tuplify(...elements: T) {
return elements;
}function useArray() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return [numberValue, functionValue]; // type is (number | (() => void))[]
}function useTuple() {
const numberValue = useRef(3).current;
const functionValue = useRef(() => {}).current;
return tuplify(numberValue, functionValue); // type is [number, () => void]
}
```ํ์ง๋ง React team์ ๋๊ฐ ์ด์์ ๊ฐ์ returnํ๋ custom hook์ tuple ๋์ ์ ์ ํ object๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค๋ ๊ฒ์ ์ฃผ์ํ์ธ์.
#### ๋ ๋ง์ Hooks + TypeScript ์ ๊ดํ ์ฝ์ ๊ฑฐ๋ฆฌ:
- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d
- https://fettblog.eu/typescript-react/hooks/#useref๋ง์ฝ React Hooks library๋ฅผ ์์ฑํ๊ณ ์๋ค๋ฉด, ์ฌ์ฉ์๋ค์ด ์ฌ์ฉํ ์ ์๋๋ก types๋ฅผ export ํด์ผ ํ๋ค๋ ๊ฒ์ ์์ง ๋ง์ธ์.
#### React Hooks + TypeScript Libraries ์์:
- https://github.com/mweststrate/use-st8
- https://github.com/palmerhq/the-platform
- https://github.com/sw-yx/hooks[์ถ๊ฐํ ๋ด์ฉ์ด ์๋์? issue๋ฅผ ์ฅ์ฑํ์ธ์!](https://github.com/typescript-cheatsheets/react/issues/new).
#### Class Components
TypeScript์์ `React.Component`๋ generic type (aka `React.Component`)์ ๋๋ค. ๋ฐ๋ผ์ `React.Component`์ prop๊ณผ state type parameter๋ฅผ ์ ๋ฌํด์ผ ํฉ๋๋ค. :
```tsx
type MyProps = {
// `interface`๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ ๊ด์ฐฎ์ต๋๋ค
message: string;
};
type MyState = {
count: number; // ์ด๋ฐ ์์ผ๋ก
};
class App extends React.Component {
state: MyState = {
// ๋ ๋์ ํ์ ์ถ๋ก ์ ์ํด ์ ํ์ ์ผ๋ก ์์ฑํ ๋ ๋ฒ์งธ annotation
count: 0,
};
render() {
return (
{this.props.message} {this.state.count}
);
}
}
```[TypeScript Playground ํ์ธํด ๋ณด๊ธฐ](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA)
์ฌ์ฌ์ฉํ๊ธฐ ์ํด ์ด๋ฌํ types/interfaces๋ฅผ export/import/extend ํ ์ ์๋ค๋ ๊ฒ์ ์์ง ๋ง์ธ์.
์
state
๋ฅผ ๋ ๋ฒ annotate ํ ๊น์?๋ฐ๋์ `state` class property์ annotateํ ํ์๋ ์์ง๋ง, ์ด๋ ๊ฒ ํ๋ฉด `this.state`์ ์ ๊ทผํ๊ฑฐ๋ state๋ฅผ ์ด๊ธฐํ ํ ๋ ๋ ๋์ ํ์ ์ถ๋ก ์ ๊ฐ๋ฅํ๊ฒ ํฉ๋๋ค.
๊ทธ ์ด์ ๋ ๋ ๊ฐ์ annotation์ ์๋ก ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. ๋ ๋ฒ์งธ generic type parameter๋ `this.setState()`๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๋์ํ๋๋ก ํด์ค๋๋ค. ์๋ํ๋ฉด ์ด ๋ฉ์๋๋ base class์์ ์ค๊ธฐ ๋๋ฌธ์ ๋๋ค. ํ์ง๋ง ์ปดํฌ๋ํธ ๋ด์์ `state`๋ฅผ ์ด๊ธฐํ ํ๋ ๊ฒ์ base implementation์ overrideํ๊ธฐ ๋๋ฌธ์ ์ปดํ์ผ๋ฌ์๊ฒ ์ฌ์ค์ ๋ค๋ฅธ ์์ ์ ํ๊ณ ์์ง ์๋ค๋ ๊ฒ์ ์๋ ค์ค์ผ ํฉ๋๋ค. (=์ปดํ์ผ๋ฌ์๊ฒ ์ฌ์ค์ ๊ฐ์ ์์ ์ ํ๊ณ ์๋ค๋ ๊ฒ์ ์๋ ค์ค์ผ ํฉ๋๋ค.)
[์ฌ๊ธฐ์ @ferdaber์ ์๊ฒฌ์ ํ์ธํด๋ณด์ธ์](https://github.com/typescript-cheatsheets/react/issues/57).
readonly
๋ ํ์ ์๋ค์ข ์ข ์ํ ์ฝ๋์ props์ state๊ฐ ๋ณํ ์ ์๋ค๊ณ ํ์ํ๊ธฐ ์ํด `readonly`๋ฅผ ํฌํฉํ๋ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
```tsx
type MyProps = {
readonly message: string;
};
type MyState = {
readonly count: number;
};
````React.Component
`๊ฐ ์ด๋ฏธ props์ state๊ฐ ๋ณํ ์ ์๋ค๊ณ ํ์ํ๊ธฐ ๋๋ฌธ์ ์ถ๊ฐ์ ์ผ๋ก readonlyํ์๋ฅผ ํ ํ์๊ฐ ์์ต๋๋ค. ([PR ๊ณผ discussion์ ํ์ธํ์ธ์!](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813))
**Class Methods**: ์๋ ํ๋๋ฐ๋ก ํ๋, ๋น์ ์ ํจ์๋ฅผ ์ํ ๋ชจ๋ arguments๋ type์ด ์์ด์ผ ํ๋ค๋ ๊ฒ๋ง ๊ธฐ์ตํ์ธ์.
```tsx
class App extends React.Component<{ message: string }, { count: number }> {
state = { count: 0 };
render() {
return (
this.increment(1)}>
{this.props.message} {this.state.count}
);
}
increment = (amt: number) => {
// ์ด๋ฐ ์์ผ๋ก
this.setState((state) => ({
count: state.count + amt,
}));
};
}
```[TypeScript Playground์์ ํ์ธํด ๋ณด๊ธฐ](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA)
**Class Properties**: ๋ง์ฝ ๋์ค์ ์ฌ์ฉํ๊ธฐ ์ํด class properties๋ฅผ ์ ์ธํ๋ค๋ฉด, `state`์ ๊ฐ์ด ์ ์ธํ๋ ํ ๋น์ ํ์ง ์์ต๋๋ค.
```tsx
class App extends React.Component<{
message: string;
}> {
pointer: number; // ์ด๋ฐ ์์ผ๋ก
componentDidMount() {
this.pointer = 3;
}
render() {
return (
{this.props.message} and {this.pointer}
);
}
}
```[TypeScript Playground์์ ํ์ธํด ๋ณด๊ธฐ](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA)
[์ถ๊ฐํ ๋ด์ฉ์ด ์๋์? issue๋ฅผ ์์ฑํ์ธ์!](https://github.com/typescript-cheatsheets/react/issues/new).
#### getDerivedStateFromProps ํ์ ํ(Typing) ํ๊ธฐ
`getDerivedStateFromProps`๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์, [documentation](https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops)๊ณผ [You Probably Don't Need Derived State](https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html)๋ฅผ ์ฝ์ด๋ณด์ธ์. Derived State๋ memoization์ ์ค์ ํ๋ ๊ฒ์ ๋์ธ ์ ์๋ hooks์ ์ฌ์ฉํ์ฌ ๊ตฌํ๋ ์ ์์ต๋๋ค.
๋ค์์ `getDerivedStateFromProps`๋ฅผ annotateํ ์ ์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ ๋๋ค.
1. ๋ง์ฝ derived state์ type์ ๋ช ์์ ์ผ๋ก ์ค์ ํ๊ณ , `getDerivedStateFromProps`์ return ๊ฐ์ด ์ค์ ํ type์ ์ค์ํ๋์ง ์๊ณ ์ถ์ ๊ฒฝ์ฐ
```tsx
class Comp extends React.Component {
static getDerivedStateFromProps(props: Props, state: State): Partial | null {
//
}
}
```2. ํจ์์ return ๊ฐ์ด state๋ฅผ ๊ฒฐ์ ํ๋๋ก ํ๊ณ ์ถ์ ๊ฒฝ์ฐ
```tsx
class Comp extends React.Component> {
static getDerivedStateFromProps(props: Props) {}
}
```3. ๋ค๋ฅธ state fields์ derived state ๊ทธ๋ฆฌ๊ณ memoization์ ์ํ ๊ฒฝ์ฐ
```tsx
type CustomValue = any;
interface Props {
propA: CustomValue;
}
interface DefinedState {
otherStateField: string;
}
type State = DefinedState & ReturnType;
function transformPropsToState(props: Props) {
return {
savedPropA: props.propA, // save for memoization
derivedState: props.propA,
};
}
class Comp extends React.PureComponent {
constructor(props: Props) {
super(props);
this.state = {
otherStateField: "123",
...transformPropsToState(props),
};
}
static getDerivedStateFromProps(props: Props, state: State) {
if (isEqual(props.propA, state.savedPropA)) return null;
return transformPropsToState(props);
}
}
```[TypeScript Playground์์ ํ์ธํด ๋ณด๊ธฐ](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA)
#### `defaultProps`๊ฐ ํ์ํ์ง ์์์๋ ์์ต๋๋ค
[์ด ํธ์](https://twitter.com/dan_abramov/status/1133878326358171650)์ ๋ฐ๋ฅด๋ฉด, defaultProps๋ deprecate ๋ ๊ฒ์ ๋๋ค.. ๋ค์์ ํ ๋ก ์ ํ์ธํด ๋ณด์ธ์.:
- [Original tweet](https://twitter.com/hswolff/status/1133759319571345408)
- [์ด article](https://medium.com/@matanbobi/react-defaultprops-is-dying-whos-the-contender-443c19d9e7f1)์์ ๋ ๋ง์ ์ ๋ณด๋ฅผ ์ป์ ์ ์์ต๋๋ค.object default value๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ํต์์ ์ผ๋ก ํฉ์๋ ๋ด์ฉ์ ๋๋ค.
Function Components:
```tsx
type GreetProps = { age?: number };const Greet = ({ age = 21 }: GreetProps) => // etc
```Class Components:
```tsx
type GreetProps = {
age?: number;
};class Greet extends React.Component {
render() {
const { age = 21 } = this.props;
/*...*/
}
}let el = ;
```#### `defaultProps` ํ์ดํ ํ๊ธฐ
[TypeScript 3.0+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html)์์ ํ์ ์ถ๋ก ์ ์ฑ๋ฅ์ด ๋งค์ฐ ๋ง์ด ๋ฐ์ ๋์์ต๋๋ค. ํ์ง๋ง ์ฌ์ ํ [๋ช๋ช์ edge case ๋ค์ด ๋ฌธ์ ๊ฐ ๋๊ธฐ๋ ํฉ๋๋ค.](https://github.com/typescript-cheatsheets/react/issues/61).
**Function Components**
```tsx
// ์ฌ์ด ๋ฐฉ๋ฒ์ผ๋ก typeof๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.; hoist๋๋ ๊ฒ์ ์ฃผ์ํ์ธ์!
// DefaultProps์ ์ ์ธํ ์๋ ์์ต๋๋ค.
// e.g. https://github.com/typescript-cheatsheets/react/issues/415#issuecomment-841223219
type GreetProps = { age: number } & typeof defaultProps;const defaultProps = {
age: 21,
};const Greet = (props: GreetProps) => {
// etc
};
Greet.defaultProps = defaultProps;
```_[TS Playground์์ ํ์ธํด๋ณด๊ธฐ](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAOKVYwAKxY6ALxwA3igDmWAFxwAdgFcQAIyxQ4AXzgAyOM1YQCcACZYCyeQBte-VPVwRZqeCbOXrEAXGEi6cCdLgAJgBGABo6dXo6e0d4TixuLzgACjAbGXjuPg9UAEovAD5RXzhKGHkoWTgAHiNgADcCkTScgDpkSTgAeiQFZVVELvVqrrrGiPpMmFaXcytsz2FZtwXbOiA)_
**Class components**์ ๊ฒฝ์ฐ, ํ์ดํ์ ์ํ [๋ ๊ฐ์ง ๋ฐฉ๋ฒ](https://github.com/typescript-cheatsheets/react/pull/103#issuecomment-481061483) (including using the `Pick` utility type)์ด ์์ง๋ง, props definition์ ๊ฑฐ๊พธ๋ก ์ํ("reverse") ํ๋ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
```tsx
type GreetProps = typeof Greet.defaultProps & {
age: number;
};class Greet extends React.Component {
static defaultProps = {
age: 21,
};
/*...*/
}// Type-checks! type assertion์ด ํ์ ์์!
let el = ;
```๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ฑ์๋ฅผ ์ํ
JSX.LibraryManagedAttributes
๋์์ค์์์ ์๊ฐ๋ ๊ตฌํ์ ์ฑ ๊ฐ๋ฐ์๋ค์ด ์ฌ์ฉํ๋๋ฐ ์๋ฌด๋ฐ ๋ฌธ์ ๊ฐ ์์ต๋๋ค. ํ์ง๋ง ๋ค๋ฅธ ์ฌ๋๋ค์ด ์ฌ์ฉ(consume)ํ ์ ์๋๋ก `GreetProps`๋ฅผ export ํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ์ฌ๊ธฐ์ `GreetProps`๊ฐ ์ ์๋๋ ๋ฐฉ๋ฒ์ด ๋ฌธ์ ๊ฐ ๋ฉ๋๋ค. `age`๋ ๊ผญ ํ์ํ์ง ์์ ๋์๋ `defaultProps`๋๋ฌธ์ ํ์์ ์ธ props๊ฐ ๋ฉ๋๋ค.
[`GreetProps`๋ ๋น์ ์ ์ปดํฌ๋ํธ๋ฅผ ์ํ _๋ด๋ถ์ ์ธ_ ๊ท์น(์ปดํฌ๋ํธ๊ฐ ๊ตฌํํ๋ ๊ฒ)์ด์ง, _์ธ๋ถ์ ์ธ_ ๊ฒ์ด ์๋๋๋ค](https://github.com/typescript-cheatsheets/react/issues/66#issuecomment-453878710). ๋ฐ๋ผ์ export๋ฅผ ์ํ type์ ๋ฐ๋ก ๋ง๋ค ๊ฑฐ๋, `JSX.LibraryManagedAttributes` utility๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
```tsx
// internal contract(๋ด๋ถ์ ์ธ ๊ท์น), export ๋์ด์๋ ์๋๋ค
type GreetProps = {
age: number;
};class Greet extends Component {
static defaultProps = { age: 21 };
}// external contract(์ธ๋ถ์ ์ธ ๊ท์น)
export type ApparentGreetProps = JSX.LibraryManagedAttributes;
```์ด๋ ๊ฒ ํ๋ฉด ์ฝ๋๊ฐ ์ ์คํ๋์ง๋ง, `ApparentGreetProps`๋ฅผ ์ฌ์ฉํ๋๊ฒ ๋ค์ ๋ฒ๊ฑฐ๋ก์ธ ์ ์์ต๋๋ค. ์๋์์ ์ค๋ช ํ๋ `ComponentProps` utility๋ก boilerplate๋ฅผ ๊ฐ๋จํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
#### defaultProps๊ฐ ์๋ Component์ props๋ฅผ ์ฌ์ฉ(consume)ํ๊ธฐ
`defaultProps`๊ฐ ์๋ ์ปดํฌ๋ํธ๋ ์ค์ ๋ก๋ ๊ทธ๋ ์ง ์์ง๋ง ํ์์ ์ธ props๋ฅผ ๊ฐ์ง๋ ๊ฒ์ฒ๋ผ ๋ณด์ผ ์ ์์ต๋๋ค.
##### Problem Statement
๋ค์๊ณผ ๊ฐ์ ์์ ์ ํ๊ณ ์ถ๋ค๋ฉด,
```tsx
interface IProps {
name: string;
}
const defaultProps = {
age: 25,
};
const GreetComponent = ({ name, age }: IProps & typeof defaultProps) => (
{`Hello, my name is ${name}, ${age}`}
);
GreetComponent.defaultProps = defaultProps;const TestComponent = (props: React.ComponentProps) => {
;
return
};// 'age' property๋ '{ name: string; }'์ ์์ง๋ง, '{ age: number; } type์์๋ ํ์์ ์ธ property ์ ๋๋ค.
const el = ;
```##### Solution
`JSX.LibraryManagedAttributes`๋ฅผ ์ ์ฉํ๋ ์ ํธ๋ฆฌํฐ๋ฅผ ์ ์ํฉ๋๋ค.
```tsx
type ComponentProps = T extends React.ComponentType | React.Component
? JSX.LibraryManagedAttributes
: never;const TestComponent = (props: ComponentProps) => {
;
return
};// No error
const el = ;
```[_TS Playground์์ ํ์ธํด ๋ณด๊ธฐ_](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAMImQB2W3MABWJhUAHgAqAPjgBeOOLhYAHjD4ATdNjwwAdJ3ARe-cSyyjg3AlihwB0gD6Yqu-Tz4xzl67cl04cAH44ACkAZQANHQAZYAAjKGQoJgBZZG5kAHMsNQBBGBgoOIBXVTFxABofPzgALjheADdrejoLVSgCPDYASSEIETgAb2r0kCw61AKLDPoAXzpcQ0m4NSxOooAbQWF0OWH-TPG4ACYAVnK6WfpF7mWAcUosGFdDd1k4AApB+uQxysO4LM6r0dnAAGRwZisCAEFZrZCbbb9VAASlk0g+1VEamADUkgwABgAJLAbDYQSogJg-MZwYDoAAkg1GWFmlSZh1mBNmogA9Di8XQUfQHlgni8jLpVustn0BnJpQjZTsWrzeXANsh2gwbstxFhJhK3nIPmAdnUjfw5WIoVgYXBReKuK9+JI0TJpPs4JQYEUoNw4KIABYARjgvN8VwYargADkIIooMQoAslvBSe8JAbns7JTSsDIyAQIBAyOHJDQgA)
#### ์ฌ๋ฌ๊ฐ์ง ํ ๋ก ๊ณผ ์ง์
React.FC
๋ ์defaultProps
์ด ๋์ํ์ง ๋ชปํ๊ฒ ๋ง๋ค๊น์?๋ค์์ ํ ๋ก ์ ํ์ธํด ๋ณด์ธ์.:
- https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680
- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695
- https://github.com/typescript-cheatsheets/react/issues/87์ด๊ฑด ํ์ฌ ์ํ์ผ ๋ฟ์ด๊ณ , ์ถํ์๋ ์ฌ๋ฐ๋ฅด๊ฒ ์์ ๋ ๊ฒ์ ๋๋ค.
TypeScript 2.9 and earlier
TypeScript 2.9์ ๊ทธ ์ด์ ๋ฒ์ ์์๋ ์ด๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด ๋ค์ํฉ๋๋ค. ํ์ง๋ง ๋ค์ ๋ฐฉ๋ฒ์ด ์ฌํ๊น์ง ํ์ธํ ๋ฐฉ๋ฒ ์ค ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ ๋๋ค.
```ts
type Props = Required & {
/* ์ถ๊ฐ์ ์ธ props */
};export class MyComponent extends React.Component {
static defaultProps = {
foo: "foo",
};
}
```์ด์ ์๋ TypeScript์ `Partial type` ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ถ์ฅ ์ฌํญ์ด์๋๋ฐ, ์ด๋ ํ์ฌ ์ธํฐํ์ด์ค๊ฐ ๋ํ๋ ์ธํฐํ์ด์ค์์์ ๋ถ๋ถ์ ์ธ ๋ฒ์ ์ ์ถฉ์กฑํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์ด๋ฐ ๋ฐฉ๋ฒ์ผ๋ก type์ ๋ณ๊ฒฝํ์ง ์๊ณ defaultProps๋ฅผ ํ์ฅํ ์ ์์ต๋๋ค.
```ts
interface IMyComponentProps {
firstProp?: string;
secondProp: IPerson[];
}export class MyComponent extends React.Component {
public static defaultProps: Partial = {
firstProp: "default",
};
}
```์ด ์ ๊ทผ ๋ฐฉ๋ฒ์ ๋ฌธ์ ์ ์ `JSX.LibraryManagedAttributes`๋ก ์๋ํ๋ ํ์ ์ถ๋ก ์ ๋ณต์กํ ์ด์๋ฅผ ๋ฐ์์ํจ๋ค๋ ๊ฒ์ ๋๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก ์ปดํ์ผ๋ฌ๋ ํด๋น ์ปดํฌ๋ํธ๋ก JSX expression์ ์์ฑํ ๋ ๋ชจ๋ props๊ฐ ์ ํ์ฌํญ(optional)์ด๋ผ๊ณ ์๊ฐํฉ๋๋ค.
[@ferdaber์ ์๊ฒฌ์ ํ์ธํด ๋ณด์ธ์.](https://github.com/typescript-cheatsheets/react/issues/57) and [here](https://github.com/typescript-cheatsheets/react/issues/61).
[์ถ๊ฐํ ๋ด์ฉ์ด ์๋์? ์ด์๋ฅผ ์์ฑํ์ธ์!](https://github.com/typescript-cheatsheets/react/issues/new).