Trick 1 - Props Type Annotate

// Annotate incoming props type can be achieved with ComponentPropsWithoutRef or ComponentPropsWithRef but those are generic types, need to specify the exact type props. E.g

type InputProps = {
	someType: someType
} & ComponentPropsWithoutRef<"input">

Trick 2 - Discriminated Union Type

// You can create multiple type annotations and use them together for the same component to create more flexible components (discriminated union types)

Trick 3 - TS Accessing Properties

// Typescript is not happy with accesing the properties that doesn't exist and   it tries to prevent you to try that even the code is technically correct in JS

// Wrong Practice
if (anyProps.anyPossibleNotExistObjectProperty) // TS Warns you

// Correct way
if ('property' in anyProps)

Trick 4 - Type Predicate

// While creating a flexible common component, e.g. generic button component,and returning an HTML element based on the property, e.g. button or a/link tag, we  can create a helper function to return the value for our condition check to ren-der the html element that we want and to be more specific about it, we can create type predicate for the return value of the function to tell typescript what is  the return value for the function.

// Helper function
const htmlElemenProps = (anyprops: SOMETYPE) : anyprops is HTMLELEMENTPROPS => {
	return 'property' in anyProps
} 

NOTE

// This comes with downsize, need to be specific in this case because based  on the HTML element we want to return from our flexible common component, TS can easily get confused and doesn't support the developer on the element properties that we want to add. 
**To get** //better support from TS we can add the possible never properties on type annotations to let TS know which element cannot take those properties.

E.G.
type InputProps = {
	someType: someType,
	someType?: never
} & ComponentPropsWithoutRef<"input">

Trick 5 - Polymorphic Component

// Mostly used to share common JSX code, logic or styling as usual with the wrapper components.

// We can use ElementType from react types to accept incoming props for the JSX values.

// Example
import { type ElementType, type ReactNode } from "react";

type SomeProps = {
	some: ElementType
}

// To be more flexible about polymorphic wrapper components we can turn our prop type definition into a generic type

type SomeProps<T extends ElementType> = {
  some?: T;
  children: ReactNode;
} & ComponentPropsWithoutRef<T>;

// But in this case, TS will be confused about the component type, to get around this issue we should simply create our functional component with a generic type annotation

const OurWrapperComponent = <C extends ElementType>({
  as,
  children,
  ...props
}: SomeProps<C>)

Trick 6 - Children Type

// We can use ReactNode type coming with react to let TS know we are gonna accept incoming children with the current component.

import { type ReactNode } from "react";

type SomeProps = {
	children: ReactNode
}

const SomeComponent: FC<SomeProps> = ({children}) => {
	return <htmlElement>{children}</htmlElement>

Trick 7 - Type Import

// We can use type keyword while importing the default types from react to let  compiler know that import is just a type and can have a slightly better result  during the build process.

// Example
import { type ReactNode } from "react";