# [React] React with TypeScript 筆記
###### tags `React` `TypeScript` `前端筆記`
<div style="display: flex; justify-content: flex-end">
> created: 2024/07/22
</div>
## 說明
記錄 React 使用 TypeScript 的一些學習筆記。
## `React.ComponentPropsWithoutRef<HTMLElement>` 讓 Component 可以有對應 HTMLElement 的屬性
> 關鍵字:`Wrapping/Mirroring a HTML Element`
若想要建立一個按鈕元件,但是又想要讓該元件可以有原生 `<butto />` 的屬性的話,可以使用 `React.ComponentPropsWithoutRef<HTMLElement>` 在使用該元件時也可以透過 `props` 傳入對應 HTMLElement 的屬性:
```typescript=
// ref. https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring
import * as React from 'react';
// usage
function App() {
// Type '"foo"' is not assignable to type '"button" | "submit" | "reset" | undefined'.(2322)
// return <Button type="foo"> sldkj </Button>
// no error
return <Button type='button'> text </Button>
}
// implementation 1
// export interface ButtonProps extends React.ComponentProps<'button'> {
// specialProp?: string;
// }
// implementation 2
type ButtonProps = {
specialProp?: string
} & React.ComponentPropsWithoutRef<'button'>
export function Button(props: ButtonProps) {
const { specialProp, ...rest } = props;
// do something with specialProp
return <button {...rest} />;
}
```
[TS Playground](https://www.typescriptlang.org/play/?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcA5FDvmQNwCwAUIwPTNwCuqyA5lowewB2+YBEFwAgmDAAKAJRwA3ozhxWcACoBPMFnIAiAhAj6ycYOkER4aVMG6DkAIwA2emBDgwdesvqfsMB6C+nAAPnD6qOxOIMAwoRH6lKhYCeEcggAmWATAglhZZAB0MgBMAMxlZXIqamyUMOxQ4gA8AEKBwV4+ALyGxvoAfHCoLlkA1gBWcK3MnUFiQ3V16lZwWFDEUHWNzW0L3d66vWQBi4JkIzBYAB7wc4dLjAC+jCxsoGBuIFiCMMgYKJBB8NrdILBzP9NgQ8HonoIAArEMDoO43bLobB4GDFADCJEgBX+yIgqNaZy6YiuSlBqlQulwwGQLlJYAA-AAuUYwKD5bj0BjqN5MIWfcA-P4AoFiOBlOCMY7wqlIlHoXq0hj0xnM1kork8vmCbivOAAMkwVFxBPAYilbNQAHV4gALCCBbAECnnYJXd5i8wSrC-f6A4FwCpwXojEDQPQAN02TggqQVAaVcARDqjmvU2qwTJZbINqF5-NBL3NlpxxQRAAkNABZAAyEiCfPOWFQrQbLYRAFFJf9lqLGHcIYQhCJZQiZGA1dys2qFMotXBcGJS0pRjqiyiADRwYrHlLwSsa+dk1CC1TqLKeVAkNIu-lwADurp3Bd1bN2aX2sw+rKijHsUp6VswQyCi8QA)
:::info
有 `React.ComponentPropsWithoutRef` 當然也就有 `React.ComponentPropsWithRef` 差別是有無 `props.ref`。
:::
## `type` vs `interface`
> Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an `interface` are available in `type`, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.
> *[ref. Differences Between Type Aliases and Interfaces](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces)*
基本上 `interface` 跟 `type` 的使用時機沒有硬性規定,這兩個方式都可以達到定義物件該長怎麼樣。差別是這兩個都有自己的限制:
### `inteface`
1. 不可用 `intersection(&)` 只能用 `extends`
```typescript=
interface HtmlAttributes {
disabled: boolean
}
interface ButtonHtmlAttributes extends HtmlAttributes {
type: 'Submit' | 'Button' | null
}
```
2. 重複名稱的 `interface` 會自動 merge
```typescript=
interface Test {
title: string;
}
interface Test {
ts: number;
}
let test: Test = {
title: '',
ts: 9
}
```
[TS Playground](https://www.typescriptlang.org/play/?#code/JYOwLgpgTgZghgYwgAgBJgLYBsCCYxTABGArpAM7IDeAsAFDLIAmw5cRWETAXMkQPb9OcEPQC+9SXVCRYiFACEyYfiHTY8BYmQiUIAD0ggmldbnyFSFavUZgAngAcIvAOQBlEkQzAwr5AA+yK5K+Kr+QSAkWFjiUjLQ8EjIACq6YDYMyGC+nLzkWiAA5gDccXT0CXLJaQWZduS8URhE0GV0EhV0nBkUYLy1GQC89dm5LsGuADS22Y3IAJziQA)
### `type`
1. 不可用 `extends` 只能用 `intersection(&)`
```typescript=
type HtmlAttributes = {
disabled: boolean
}
type ButtonHtmlAttributes = {
type: 'Submit' | 'Button' | null
} & HtmlAttributes
```
2. `interface` 只能定義物件,但是 `type` 可以很活
```typescript=
type isLoading = boolean
type Theme = "dark" | "light"
type Lang = "en" | "fr"
```
3. 不會像 `interface` 一樣自動 merge 相同名稱的 `interface`
```typescript=
type TestError = {
name: string
}
// Duplicate identifier 'TestError'.(2300)
type TestError = {
age: number
}
```
[TS Playground](https://www.typescriptlang.org/play/?#code/C4TwDgpgBAEsC2AbAgsYAnAlgIwK7AgGcoBeKAbwFgAoKKAE00IENtEJ6AuKbAe1-bMAdjQC+NGqEhQAQvmC8hcJKgw58RUhRp0pEbgHIAyrmzxMwA1AA+UA3LSKrtobkSIxUAGSwEKNFh4BIQS1HpQTAAyvMyMQgDmWnwCEMKS4NAAKgAWEPDQZABE9MzoANaFNlCFiJjx2cCF6dKRwolFEEKVtoUAZuhN1M1ZRMAAoujovOhaVLRQQsz53IRqCWKh4ZmjE1MzZHN0zPH6C7jw2BDoG9RAA)
## 參考資料
1. [React with TypeScript Cheatsheet](https://blog.bitsrc.io/react-with-typescript-cheatsheet-9dd891dc5bfe#d41e)