- README
- Typescript Doc
- More
- use template
$ npx create-react-app <project-name> --template typescript
- create project from scratch
- event
React.MouseEvent
React.ChangeEvent
- styles
React.CSSProperties
- HTML element
HTMLButtonElement
HTMLInputElement
- children
React.ReactNode
- pass component as prop
React.ComponentType<SourceComponentType>
- Class component
- Generic
type CustomPropsType<T> = {}
const Component = <T extends {}>(props: CustomPropsType<T>) => {}
- Restricting props
- Template literals & Exclude
Exclude<UnionType, ExcludedMembers>
- Wrapping HTML & Omit
React.ComponentProps<htmlString | typeof CustomPropsType>
Omit<Type, Keys>
- Extracting component prop type
- Polymorphic component
- basic
- primitive value - source code
- object - source code
- array - source code
- advanced
- status condional display - source code
children props
withReact.ReactNode
- source code- @type/react version 16: import React at the top
- @type/react version 17: just use it for typescript
question mark '?'
for optional parameter - source code- NOTE: About
'?'
- To mention that a particular variable is optional.
- To pre-check if a member variable is present for an object.
- event
<button>
- click event,
React.MouseEvent
- source code - NOTE: event more specific for html button -
React.MouseEvent<HTMLButtonElement>
- click event,
<input>
- change event,
React.ChangeEvent
- source code
- change event,
- style
React.CSSProperties
- More
- destructure properties
- export type - source code
- import type - source code
- Conclution
- useState
- future value - source code
const [user, setUser] = useState<UserType | null>(initialValue) const getValue = () => { const name = user?.name }
- type assertion - source code
- access
user.name
without a check - If we're confident
user
will be initialized soon after setup, and always have value after, we can use type assertion
const [user, setUser] = useState<UserType>({} as UserType) const getValue = () => { const name = user.name }
- access
- future value - source code
- useReducer
- source code
- more specific:
union type
foraction.type
- handle having payload or not:
ActionType = UpdateType | ResetType
- useContext
- basic
- provider - type for the children node
React.ReactNode
- source code - create provider
- source code - contain app
- source code - use context
- provider - type for the children node
- future value
type AuthUser
- user inputtype UserContextProviderType
- children componenttype UserContextType
- createContext- source code - userContextProvider
- source code - contain app
- source code - use context
- while createContext(), use
type assertion
, children don't have to checkuserContext
while using it// createContext const UserContext = createContext({} as UserContextType)
// children componet const userContext = useContext(UserContext) const setData = () => { userContext.setUser(null) } const getData = () => { console.log(userContext.user?.name) }
- basic
- useRef
- html element
- handle
useRef()
type- e.g. reference for input elemnt:
HTMLInputElement
- e.g. reference for input elemnt:
- source code - html element
- handle
- mutable ref
- handle
uesRef()
type- e.g.
const intervalRef = useRef<number | null>(null)
- e.g.
- check ref exist before clearInterval
- e.g.
if (intervalRef.current) window.clearInterval(intervalRef.current)
- e.g.
- source code - mutable ref
- handle
- html element
- handle type of the component's props & state
class ClassComponent extends React.Component<ClassComponentProps, ClassComponentState> {}
- source code
- pass in a component props -
React.ComponentType
- the component itself accept props -
React.ComponentType<AcceptPropsType>
- source code - pass component as props
- mention generic type
T
(NOTE: use any lable is OK) after type variabletype ListProps<T> = {}
- it means
ListProps
accepts a variable callT
- assign
T
type ListProps = { item: string[] | number[] }
->type ListProps<T> = { item: T[] }
- use the type
const ListComponent = (props: ListProps<T>) => {}
- specify what
T
can be before the parenthesesT
needs to extend a base typeconst ListComponent = <T extends {}>(props: ListProps<T>) => {}
- then we can pass in an array of any type at
ListComponent
, and will not throw an error - source code - generic props
-
generate basic value type
type ValueType = { value: number; }
-
extend type for
isPositive
etc.type isPositiveType = ValueType & { isPositive: boolean; isNegative?: never; isZero?: never; }
-
use in component props type
type ComponentPropsType = isPositiveType | isNegativeType | isZeroType
-
usage
<RestrictingPropsComponent value={10} isPositive />
- build type for
horizontal
&vertical
type HorizontalPosition = 'left' | 'center' | 'right';
type VerticalPosition = 'top' | 'center' | 'bottom';
- exclude
center-center
- Exclude<
${HorizontalPosition}-${VerticalPosition}
, 'center-center`>
- Exclude<
- add 'center`
type Postion = ExclusivePostion | 'center'
- source code - template literals and exclude
- wrapping
-
type CustomButtonPropsType = { specificTypeEgTypeForClass: string } & React.ComponentProps<'button'>
-
usage
const CustomButton = ({ classname, children, ...restProps }: CustomButtonPropsType) => { return <button className={classname} {...restProps}>{ children }</button> }
-
- Omit - omit takes an object type and removes the specific properties
-
scenario - restrict children type to just string
-
define
children
for string -
omit the type we want to omit in
React.ComponentProps
type ComponentPropsType = { className: 'primary' | 'secondary'; children: string; } & Omit<React.ComponentProps<'button'>, 'children'>
-
-
React.ComponentProps<typeof Component>
import SourceComponent from 'path/to/source/component' const AnotherComponent = (props: React.ComponentProps<typeof SourceComponent>) => {}
-
scenario - passing a property controls what HTML element is rendered in the children component
- goal
import Text from 'path/to/text/component' const App = () => { return ( <Text as="h1">context</Text> ) } // should be: <h1>context</h1>
- goal
-
implement steps
-
initialize
type TextPropsType = { size?: 'sm' | 'md' | 'lg'; color?: 'primary' | 'secondary'; children: React.ReactNode; as?: string } function Text({ size, color, children, as }: TextPropsType) { const Component = as || 'div' return ( <Component className={`class-width-${size}-${color}`}>{ children }</Component> ); }
// Error: Type '{ children: ReactNode; className: string; }' is not assignable to type 'IntrinsicAttributes'. Property 'children' does not exist on type 'IntrinsicAttributes'.
-
as
props can't be any random string ->React.ElementType
-
improve -
label
should havehtmlFor
attributetype TextOwnPropsType<E extends React.ElementType> = { size?: 'sm' | 'md' | 'lg'; color?: 'primary' | 'secondary'; children: React.ReactNode; as?: E } type TextPropsType<E extends React.ElementType> = TextOwnPropsType<E> & React.ComponentProps<E>
-
children might collide with children with
div
tag -> omit the keys that are specified as part of theTextOwnPropsType
type TextPropsType<E extends React.ElementType> = TextOwnPropsType<E> & Omit<React.ComponentProps<E>, keyof TextOwnPropsType<E>>
-
add generic type for component
const Text = <E extends React.ElementType = 'div'>({ size, color, children, as }: TextPropsType<E>) => { const Component = as || 'div' return ( <Component className={`class-width-${size}-${color}`}>{ children }</Component> ); }
-
summary
type TextOwnPropsType<E extends React.ElementType> = { size?: 'sm' | 'md' | 'lg'; color?: 'primary' | 'secondary'; children: React.ReactNode; as?: E; } type TextPropsType<E extends React.ElementType> = TextOwnPropsType<E> & Omit<React.ComponentProps<E>, keyof TextOwnPropsType<E>> const Text = <E extends React.ElementType = 'div'>({ size, color, children, as }: TextPropsType<E>) => { const Component = as || 'div' return ( <Component className={`class-width-${size}-${color}`}>{ children }</Component> ); }
-