Шпаргалка по React с TypeScript
Как типизировать React props
Содержание статьи:
- 1 Как типизировать React props
- 2 Создание алиаса для props
- 3 Типизация дополнительных props
- 4 Список типов для props компонента React
- 5 Как типизировать функциональные компоненты React
- 6 Как типизировать хуки React
- 7 Типизация хука useState
- 8 Типизация хуков useEffect и useLayoutEffect
- 9 Типизация хука useContext
- 10 Типизация хука useRef
- 11 Типизация хука useMemo
- 12 Типизация хука useCallback
- 13 Типизация пользовательских хуков
- 14 Типизация HTML-событий и форм
- 15 Понимание различных типизаций для компонентов React
- 16 Когда использовать каждый тип?
- 17 Как типизировать (расширять) HTML-элементы
- 18 ComponentPropsWithoutRef против [Element] HTMLAttributes
- 19 Когда использовать type и interface?
Поскольку React props используются для отправки данных от одного компонента React в другой, существует множество типов, которые вы можете использовать для типизации props. Чтобы записать типы props, нужно добавить двоеточие и буквенное обозначение (: {}) рядом с деструктурирующим назначением дочерних props в объявлении компонента React. Вот пример типизации string и number:
JavaScript const App = ({ title, score }: { title: string, score: number }) => ( <h1>{title} = {score}</h1> )
123 | const App = ({ title, score }: { title: string, score: number }) => ( <h1>{title} = {score}</h1>) |
Создание алиаса для props
Поскольку в React принято записывать один компонент в один файл .js или .jsx, вы можете объявить алиас type для props компонента, чтобы упростить чтение кода. Вот пример создания алиаса type для props компонента App:
React JS. Основы
Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения
Получить курс сейчас!
JavaScript type Props = { title: string, score: number } const App = ({ title, score }: Props) => ( <h1>{title} = {score}</h1> )
12345678 | type Props = { title: string, score: number} const App = ({ title, score }: Props) => ( <h1>{title} = {score}</h1>) |
Как видите, type для компонента props избавит вас от необходимости включать типы props в одну строку.
Типизация дополнительных props
Вы можете сделать props необязательными, добавив символ вопросительного знака «?» после имени props. В следующем примере props для title становятся необязательными:
JavaScript type Props = { title?: string, score: number }
1234 | type Props = { title?: string, score: number} |
Необязательность для props означает, что вы можете визуализировать компонент без передачи props, но когда вы передаете props, они должна быть объявленного типа.
Список типов для props компонента React
Теперь, когда вы знаете, как проверить тип props, вот список общих типов, которые вы можете использовать в своем приложении React. Во-первых, у вас есть примитивные типы, такие как string, number и boolean, как показано ниже:
JavaScript type Props = { // primitive types title: string, score: number, isWinning: boolean }
123456 | type Props = { // primitive types title: string, score: number, isWinning: boolean} |
Вы также можете создать массив одного типа, добавив буквенное обозначение массива ([]) после типа следующим образом:
JavaScript type Props = { title: string[], // an array of string score: number, isWinning: boolean }
12345 | type Props = { title: string[], // an array of string score: number, isWinning: boolean} |
Также, вы можете указать значения, которые могут быть приняты для props. Вам нужно разделить литералы с помощью оператора вертикальной черты «|» как показано ниже:
JavaScript type Props = { priority: «high» | «normal» | «low», score: 5 | 9 | 10 }
1234 | type Props = { priority: «high» | «normal» | «low», score: 5 | 9 | 10} |
TypeScript выдаст статическую ошибку, если указанное выше значение priority или score не соответствует ни одному из литеральных значений. Также вы можете добавить объект в props следующим образом:
JavaScript type Props = { user: { username: string, age: number, isMember: boolean } }
1234567 | type Props = { user: { username: string, age: number, isMember: boolean }} |
Если у вас есть массив объектов, просто добавьте нотацию массива в конце объявления следующим образом:
JavaScript type Props = { user: { username: string, age: number, isMember: boolean }[] // right here }
1234567 | type Props = { user: { username: string, age: number, isMember: boolean }[] // right here} |
React props также могут иметь такие функции, как onClick и onChange. Вы можете типизировать параметры, принимаемые функцией, или взять объект event из HTML, как показано ниже:
JavaScript type Props = { // function that returns nothing onClick: () => void, // function accepts a parameter and has return type onChange: (target: string) => boolean, // function that takes an event handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void }
12345678 | type Props = { // function that returns nothing onClick: () => void, // function accepts a parameter and has return type onChange: (target: string) => boolean, // function that takes an event handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void} |
Если вы объявляете функцию onChange в теле компонента, вы можете сразу же проверить параметр и типы возвращаемых значений, как показано ниже:
JavaScript const App = () => { const [message, setMessage] = useState(«») const onChange = (e: React.FormEvent<HTMLInputElement>): void => { setMessage(e.currentTarget.value); } // code omitted for clarity.. }
12345678910 | const App = () => { const [message, setMessage] = useState(«») const onChange = (e: React.FormEvent<HTMLInputElement>): void => { setMessage(e.currentTarget.value); } // code omitted for clarity..} |
Наконец, компоненты React могут принимать другой компонент в качестве дочерних props, поэтому вам нужно использовать ReactNode для типизации этих дочерних props:
JavaScript type Props = { children: React.ReactNode } const App = ({ children }: Props) => ( <div>{children}</div> )
1234567 | type Props = { children: React.ReactNode} const App = ({ children }: Props) => ( <div>{children}</div>) |
И это наиболее распространенные типы, которые вы можете использовать для React props. Давайте научимся типизировать функциональные компоненты React!
Как типизировать функциональные компоненты React
Библиотека TypeScript’s Definitely Typed включект React.FunctionComponent (или React.FC для краткости), которые можно использовать для типизации функциональных компонентов React.
Вы можете комбинировать type Props и тип React.FC, чтобы создать типобезопасный функциональный компонент props следующим образом:
JavaScript type Props = { title: string } const App: React.FC<Props> = ({title}) => { return ( <h1>{title}</h1> ) }
123456789 | type Props = { title: string} const App: React.FC<Props> = ({title}) => { return ( <h1>{title}</h1> )} |
Когда вы вызываете компонент App, вам нужно будет указать message с типом string. Но поскольку TypeScript может определить тип вашей переменной, вы можете убрать типизацию компонента с помощью React.FC следующим образом:
JavaScript type Props = { title: string } const App = ({ title }: Props) => <div>{title}</div> // App type will be inferred
123456 | type Props = { title: string} const App = ({ title }: Props) => <div>{title}</div>// App type will be inferred |
Если у вас есть несколько props для компонента, вы даже можете типизировать props одной строкой, как показано ниже, избавившись от необходимости создавать тип Props:
JavaScript const App = ({ title }: { title: string }) => <div>{title}</div>
1 | const App = ({ title }: { title: string }) => <div>{title}</div> |
Благодаря функции предполагаемого типа TypeScript вам вообще не нужно типизировать функциональные компоненты React.
Как типизировать хуки React
Хуки React поддерживаются библиотекой @types/react начиная с версии 16.8. Как правило, Typescript должен иметь возможность задать тип для ваших хуков, если у вас нет особых случаев, когда тип должен быть объявлен явно. Давайте посмотрим, как типизировать хуки React, и начнем с хука useState.
Типизация хука useState
Значение useState может быть определено из начального значения, которое вы устанавливаете при вызове функции.
Например, следующий вызов useState() инициализирует состояние пустой строкой. При вызове функции setState нужно передать строку или будет ошибка:
JavaScript const App = () => { const [title, setTitle] = useState(«») // type is string const changeTitle = () => { setTitle(9) // error: number not assignable to string! } }
1234567 | const App = () => { const [title, setTitle] = useState(«») // type is string const changeTitle = () => { setTitle(9) // error: number not assignable to string! }} |
Но когда вам нужно инициализировать состояние такими значениями, как null или undefined, тогда нужно добавить generic при инициализации состояния. Generic позволяет использовать несколько типов для хука useState, как показано ниже:
JavaScript // title is string or null const [title, setTitle] = useState<string | null>(null) // score is number or undefined const [score, setScore] = useState<number | undefined>(undefined)
12345 | // title is string or nullconst [title, setTitle] = useState<string | null>(null) // score is number or undefinedconst [score, setScore] = useState<number | undefined>(undefined) |
Когда у вас есть сложный объект в качестве значения состояния, вы можете создать interface или type для этого объекта следующим образом:
JavaScript interface Member { username: string, age?: number } const [member, setMember] = useState<Member | null>(null)
123456 | interface Member { username: string, age?: number} const [member, setMember] = useState<Member | null>(null) |
Вот так вы можете типизировать хуки useState в своем приложении.
Типизация хуков useEffect и useLayoutEffect
Вам не нужно типизировать хуки useEffect и useLayoutEffect, поскольку они не имеют возвращаемого значениями. Функция очистки для хука useEffect также не имеет значения, которое можно изменить. Вы можете писать эти хуки как обычно.
Типизация хука useContext
Тип хука useContext обычно определяется из начального значения, которое вы передали в функцию createContext() следующим образом:
JavaScript const AppContext = createContext({ authenticated: true, lang: ‘en’, theme: ‘dark’ }) const MyComponent = () => { const appContext = useContext(AppContext) //inferred as an object return <h1>The current app language is {appContext.lang}</h1> }
12345678910 | const AppContext = createContext({ authenticated: true, lang: ‘en’, theme: ‘dark’}) const MyComponent = () => { const appContext = useContext(AppContext) //inferred as an object return <h1>The current app language is {appContext.lang}</h1>} |
Приведенное выше значение контекста будет выведено как следующий объект:
JavaScript { authenticated: boolean, lang: string, theme: string }
12345 | { authenticated: boolean, lang: string, theme: string} |
В качестве альтернативы вы также можете создать type, который будет служить универсальным для возвращаемого значения CreateContext. Например, предположим, что у вас есть ThemeContext, который имеет только два значения: light и dark. Вот как вы типизируете контекст:
JavaScript type Theme = ‘light’ | ‘dark’ const ThemeContext = createContext<Theme>(‘dark’)
12 | type Theme = ‘light’ | ‘dark’const ThemeContext = createContext<Theme>(‘dark’) |
Тип будет использоваться в вашем коде позже, когда вы установите значение контекста с помощью ThemeContext.Provider. Затем хук useContext определит тип из объекта контекста ThemeContext, который вы передали в качестве аргумента:
JavaScript const App = () => { const theme = useContext(ThemeContext) return <div>The theme is {theme}</div> }
1234 | const App = () => { const theme = useContext(ThemeContext) return <div>The theme is {theme}</div>} |
Типизация хука useRef
Основываясь на документации React, хук useRef обычно используется для ссылки на input элемент HTML следующим образом:
JavaScript function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type=»text» /> <button onClick={onButtonClick}>Focus the input</button> </> ); }
12345678910111213 | function TextInputWithFocusButton() { const inputEl = useRef(null); const onButtonClick = () => { // `current` points to the mounted text input element inputEl.current.focus(); }; return ( <> <input ref={inputEl} type=»text» /> <button onClick={onButtonClick}>Focus the input</button> </> );} |
Вы можете написать общий вариант использования, который принимает HTMLInputElement, как показано ниже:
JavaScript const inputRef = useRef<HTMLInputElement>(null)
1 | const inputRef = useRef<HTMLInputElement>(null) |
Вам не нужно добавлять null к типу дженерика, потому что HTMLInputElement принимает уже одно из двух значений: HTMLInputElement | null.
Типизация хука useMemo
Хук useMemo возвращает мемоизированное значение, поэтому тип будет определятся возвращенным значением:
JavaScript const num = 24 // inferred as a number from the returned value below const result = useMemo(() => Math.pow(10, num), [num])
123 | const num = 24// inferred as a number from the returned value belowconst result = useMemo(() => Math.pow(10, num), [num]) |
Типизация хука useCallback
Хук useCallback возвращает мемоизированную функцию обратного вызова, поэтому тип будет определятся из значения, возвращаемого функцией обратного вызова:
JavaScript const num = 9 const callbackFn = useCallback( (num: number) => { return num * 2 // type inferred as a number }, [num])
1234567 | const num = 9 const callbackFn = useCallback( (num: number) => { return num * 2 // type inferred as a number }, [num]) |
Типизация пользовательских хуков
Поскольку пользовательские хуки являются функциями, вы можете добавлять явные типы для их параметров, одновременно определяя их тип из возвращаемого значения.
React JS. Основы
Изучите основы ReactJS на практическом примере по созданию учебного веб-приложения
Получить курс сейчас! JavaScript function useFriendStatus(friendID: number) { const [isOnline, setIsOnline] = useState(false); // code for changing the isOnline state omitted.. return isOnline; } const status = useFriendStatus(9) // inferred type boolean
12345 | function useFriendStatus(friendID: number) { const [isOnline, setIsOnline] = useState(false); // code for changing the isOnline state omitted.. return isOnline;} const status = useFriendStatus(9) // inferred type boolean |
Когда вы возвращаете массив, подобно как в useState, вам нужно указать возвращаемое значение как const, чтобы TypeScript не типизировал ваш тип как объединение:
JavaScript function useCustomHook() { return [«Hello», false] as const }
123 | function useCustomHook() { return [«Hello», false] as const } |
Без указания const -TypeScript будет типизировать возвращаемые значения как (string | boolean) [] вместо [string, boolean]
Вот как можно типизировать хуки React. Давайте теперь узнаем, как типизировать HTML-события и формы.
Типизация HTML-событий и форм
TypeScript может правильно определить большинство типов событий HTML, поэтому вам не нужно явно указывать тип. Например, событие onClick элемента кнопки будет определено TypeScript как React.MouseEvent:
JavaScript const App = () => ( <button onClick={ (e) => console.log(«Clicked»)}>button</button> // ^^^ e inferred as React.MouseEvent<HTMLButtonElement, MouseEvent> )
1234 | const App = () => ( <button onClick={ (e) => console.log(«Clicked»)}>button</button> // ^^^ e inferred as React.MouseEvent<HTMLButtonElement, MouseEvent>) |
Для HTML-форм вам нужно будет указать тип onSubmit как React.FormEvent, потому что интерфейс по умолчанию Any выдаст ошибку. Вот пример формы React в TypeScript:
JavaScript const App = () => { const [email, setEmail] = useState(«»)const handleSubmit = (e: React.FormEvent) => { e.preventDefault() // handle submission here… alert(`email value: ${email}`) } return ( <form onSubmit={handleSubmit}> <div> <label> Email: <input type=»email» name=»email» onChange={(e) => setEmail(e.currentTarget.value)} // ^^^ onChange inferred as React.ChangeEvent /> </label> </div> <div> <input type=»Submit» value=»Submit» /> </div> </form> ) }
1234567891011121314151617181920212223242526 | const App = () => { const [email, setEmail] = useState(«»)const handleSubmit = (e: React.FormEvent) => { e.preventDefault() // handle submission here… alert(`email value: ${email}`) } return ( <form onSubmit={handleSubmit}> <div> <label> Email: <input type=»email» name=»email» onChange={(e) => setEmail(e.currentTarget.value)} // ^^^ onChange inferred as React.ChangeEvent /> </label> </div> <div> <input type=»Submit» value=»Submit» /> </div> </form> )} |
Понимание различных типизаций для компонентов React
Хотя TypeScript может определить тип возвращаемого значения функциональных компонентов React, у вас может быть проект с правилом линтинга, которое требует, чтобы тип возвращаемого значения был явно определен. Библиотека @types/react имеет несколько типов, которые вы можете использовать для определения типа возвращаемого значения функциональных компонентов React. Вот они: ReactElement, JSX.Element,ReactNode.
Этот раздел посвящен тому, чтобы помочь вам разобраться в этих типах и в том, когда их использовать.
ReactElement — это интерфейс для объекта с типом, props и ключевыми свойствами, как показано ниже:
JavaScript type Key = string | numberinterface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> { type: T; props: P; key: Key | null; }
12345 | type Key = string | numberinterface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> { type: T; props: P; key: Key | null;} |
JSX.Element — это расширение ReactElement, в котором реализованы тип <T> и свойства <P>, как вы можете увидеть в репозитории:
JavaScript declare global { namespace JSX { interface Element extends React.ReactElement<any, any> { } } }
12345 | declare global { namespace JSX { interface Element extends React.ReactElement<any, any> { } }} |
Тип для ReactElement более строгий, чем в JSX.Element, но по сути они такие же. Наконец, ReactNode — это очень свободный тип, поскольку он включает все, что может быть возвращено методом render() компонентов класса React. В репозитории ReactNode определяется так:
JavaScript type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
1 | type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; |
Вот почему, когда ваш компонент имеет дочерние props, которые может получать другой компонент, рекомендуется использовать ReactNode в качестве типа, потому что он может получать все, что может быть отображено с помощью React.
С другой стороны, ReactElement и JSX.Element более строги по сравнению с ReactNode, поскольку не позволяют возвращать такие значения, как null.
Когда использовать каждый тип?
Тип ReactNode лучше всего использовать для типизации дочерних props, которые могут получать другой компонент React или элементы JSX, например:
JavaScript const App = ({ children }: { children: React.ReactNode }) => { return <div>{children}</div> } // At index.tsx <App> <Header/> <h2>Another title</h2> </App>
123456789 | const App = ({ children }: { children: React.ReactNode }) => { return <div>{children}</div>} // At index.tsx<App> <Header/> <h2>Another title</h2></App> |
Это связано с тем, что оба типа ReactElement и JSX.Element более строги к типу возвращаемого значения (не допускают null) и ожидают, что вы вернете один элемент.
Чтобы принять как один, так и несколько дочерних элементов для этих двух типов, вам необходимо использовать ReactElement | ReactElement [] или JSX.Element | JSX.Element [] как дочерний тип.
Типы ReactElement и JSX.Element больше подходят для явного определения типа возвращаемого значения компонента React следующим образом:
JavaScript const App = () : React.ReactElement | JSX.Element => { return <div>hello</div> }
123 | const App = () : React.ReactElement | JSX.Element => { return <div>hello</div>} |
Но поскольку мы говорим здесь о лучших практиках, я рекомендую вам следовать определению интерфейса FunctionComponent в библиотеке типов, в котором используется ReactElement <any, any> | null:
JavaScript interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined; }
1234567 | interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined;} |
И поскольку JSX.Element в точности расширяет ReactElement <any, any>, вы можете определить тип возвращаемого значения функционального компонента React следующим образом:
JavaScript const App = () : JSX.Element | null => { return <div>hello</div> }
123 | const App = () : JSX.Element | null => { return <div>hello</div>} |
Таким образом, ваш компонент по-прежнему может ничего не отображать, возвращая null. Я надеюсь, что этот раздел помог вам понять различные типы, которые можно использовать для типизации компонентов React.
Как типизировать (расширять) HTML-элементы
Иногда вам нужно создать небольшой модульный компонент, который принимает атрибуты собственного HTML-элемента в качестве свойств.
Некоторые полезные компоненты, которые вы можете создать для своего приложения, — это button, img или input. Библиотека @types/response поставляется с типом ComponentPropsWithoutRef, который вы можете использовать для получения всех собственных атрибутов HTML-элемента в качестве типа для props вашего компонента.
Например, элемент button уже знает об собственном атрибуте onClick, но когда вы создаете компонент React <Button>, вам нужно определить props, используя интерфейс или такой тип:
JavaScript type ButtonProps = { children: React.ReactNode onClick: () => void } const Button = ({ children, onClick }: ButtonProps) => { return <button onClick={onClick}>{children}</button> }
12345678 | type ButtonProps = { children: React.ReactNode onClick: () => void} const Button = ({ children, onClick }: ButtonProps) => { return <button onClick={onClick}>{children}</button>} |
В приведенном выше примере вам нужно добавить еще один props в ButtonProps, как показано ниже:
JavaScript type ButtonProps = { children: React.ReactNode onClick: () => void disabled: boolean type: ‘button’ | ‘submit’ | ‘reset’ | undefined }
123456 | type ButtonProps = { children: React.ReactNode onClick: () => void disabled: boolean type: ‘button’ | ‘submit’ | ‘reset’ | undefined } |
Можно использовать тип ComponentPropsWithoutRef, так что вам не нужно добавлять эти собственные атрибуты HTML к типу по мере роста вашего приложения. Вы можете создать тип, который имеет все атрибуты button в виде таких свойств:
JavaScript type ButtonProps = React.ComponentPropsWithoutRef<«button»> const Button = ({ children, onClick, type }: ButtonProps) => { return ( <button onClick={onClick} type={type}> {children} </button> ) }
123456789 | type ButtonProps = React.ComponentPropsWithoutRef<«button»> const Button = ({ children, onClick, type }: ButtonProps) => { return ( <button onClick={onClick} type={type}> {children} </button> )} |
Тип ComponentPropsWithoutRef <»button»> имеет все props элемента HTML button. Если вы хотите создать компонент <Img>, вы можете использовать тип ComponentPropsWithoutRef <»img»>:
JavaScript type ImgProps = React.ComponentPropsWithoutRef<«img»> const Img = ({ src, loading }: ImgProps) => { return <img src={src} loading={loading} /> }
12345 | type ImgProps = React.ComponentPropsWithoutRef<«img»> const Img = ({ src, loading }: ImgProps) => { return <img src={src} loading={loading} />} |
Вам нужно только изменить общий тип ComponentPropsWithoutRef <T>, чтобы расширить различные элементы HTML. Например:
ComponentPropsWithoutRef <’img’> для расширения элемента <img>
ComponentPropsWithoutRef <’button’> для расширения элемента <button>
ComponentPropsWithoutRef <’a’> для расширения элемента <a>
И так далее. Когда вам нужно добавить пользовательский prop, которого нет в HTML-элементе, вы можете создать интерфейс, расширяющий собственные атрибуты, следующим образом:
JavaScript interface ImgProps extends React.ComponentPropsWithoutRef<«img»> { customProp: string; } const Img = ({ src, loading, customProp }: ImgProps) => { // use the customProp here.. return <img src={src} loading={loading} />; }
12345678 | interface ImgProps extends React.ComponentPropsWithoutRef<«img»> { customProp: string;} const Img = ({ src, loading, customProp }: ImgProps) => { // use the customProp here.. return <img src={src} loading={loading} />;} |
Это особенно полезно, если вам нужен специальный prop для определения внешнего вида вашего компонента. В следующем примере пользовательский prop color используется для определения атрибута CSS style:color элемента h1:
JavaScript interface headerProps extends React.ComponentPropsWithoutRef<«h1»> { variant: «primary» | «secondary»; } const Header = ({ children, variant }: headerProps) => { return ( <h1 style={{color: variant === «primary» ? «black» : «red» }}> {children} </h1> ); };
1234567891011 | interface headerProps extends React.ComponentPropsWithoutRef<«h1»> { variant: «primary» | «secondary»;} const Header = ({ children, variant }: headerProps) => { return ( <h1 style={{color: variant === «primary» ? «black» : «red» }}> {children} </h1> );}; |
Тип ComponentPropsWithoutRef упрощает создание компонента, который является расширением элементов HTML, без необходимости самостоятельно вводить все возможные параметры prop. Вы даже можете добавить дополнительные props, расширив интерфейс.
Интерфейс ComponentPropsWithoutRef также имеет двойника под названием ComponentPropsWithRef, который вы можете использовать, когда нужно перенаправить ссылку на дочерние элементы. Узнайте больше о переадресации ссылок здесь.
ComponentPropsWithoutRef против [Element] HTMLAttributes
Если вы раньше использовали TypeScript в React, возможно, вы знакомы с интерфейсом [Element] HTMLAttributes из библиотеки @types/react, который можно использовать для расширения HTML-элементов следующим образом:
JavaScript type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> type ImgProps = React.ImgHTMLAttributes<HTMLImageElement>
123 | type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> type ImgProps = React.ImgHTMLAttributes<HTMLImageElement> |
Интерфейсы [Element]HTMLAttributes создают тот же тип, что и интерфейс ComponentPropsWithoutRef, но они более подробны, поскольку вам нужно использовать другой интерфейс и дженерик для каждого элемента HTML.
С другой стороны, ComponentPropsWithoutRef требует, чтобы вы изменили только общий тип <T>. Оба подходят для расширения HTML-элементов в компонентах React. Вы можете увидеть объяснение автора библиотеки здесь.
Когда использовать type и interface?
И type, и interface в TypeScript могут использоваться для определения свойств, компонентов и хуков React. Из справочника по TypeScript:
При использовании интерфейсов вы можете свободно расширять его следующим образом:
JavaScript interface HtmlAttributes { disabled: boolean } interface ButtonHtmlAttributes extends HtmlAttributes { type: ‘Submit’ | ‘Button’ | null }
1234567 | interface HtmlAttributes { disabled: boolean} interface ButtonHtmlAttributes extends HtmlAttributes { type: ‘Submit’ | ‘Button’ | null} |
Но типы нельзя расширять, как интерфейсы. Вам необходимо использовать символ пересечения (&) следующим образом:
JavaScript type HtmlAttributes = { disabled: boolean } type ButtonHtmlAttributes = HtmlAttributes & { type: ‘Submit’ | ‘Button’ | null }
1234567 | type HtmlAttributes = { disabled: boolean} type ButtonHtmlAttributes = HtmlAttributes & { type: ‘Submit’ | ‘Button’ | null} |
Далее, объявление интерфейса всегда является объектом, а объявление типа может иметь примитивные значения, как показано ниже:
JavaScript type isLoading = boolean type Theme = «dark» | «light» type Lang = «en» | «fr»
123 | type isLoading = booleantype Theme = «dark» | «light»type Lang = «en» | «fr» |
Ни один из приведенных выше примеров невозможен с интерфейсом, поэтому тип может быть предпочтительным для простых значений объекта. Вопрос в том, когда использовать одно вместо другого? Снова из Руководства по TypeScript:
Анализатор кода TypeScript сообщит вам, когда вам нужно использовать interface или type. Если вы не уверены, какой из них использовать, всегда выбирайте interface, пока не увидите причину использования type. Если вам нужна дополнительная информация, вот ответ StackOverflow, в котором сравниваются interface и type.
Надеюсь, эта шпаргалка будет полезна для вашего следующего проекта.
Автор: Nathan Sebhastian
Источник: webformyself.com