PropsWithChildren
allows you to define props within a children prop
import {PropsWithChildren} from 'react';
type BoxProps = PropsWithChildren<{
style: React.CSSProperties
}>;
const Box = ({children, style}: BoxProps) => {
return (
//
)
}
You also have React.ComponentPropsWithoutRef, which you could—and we will—use as follows:
type ButtonProps = React.ComponentPropsWithoutRef<'button'>;
const Button = ({ children, onClick, type }: ButtonProps) => {
return (
<button onClick={onClick} type={type}>
{children}
</button>
);
};
Now, Button has all of the some props as the native <button>
element from the DOM.
wow, useState is actually a wrapper around useReducer...I did not know that.
wow, event.target.valueAsNumber
is a thing, and super browser-supported...huh.
checkout mirage.js --> library that simulaates an api server...ex: build a UI when an API is not yet ready.
any
- could be anything
unknown
= "Idk what this is before using it"
never
- "this is nothing"
Instead of:
type Quotes = {
count: number;
onChange: ChangeEventHandler<HTMLInputElement>;
onSubmit: FormEventHandler<HTMLFormElement>;
children: React.ReactNode<children>;
}
const Quotes = ({children, count, onSubmit, onChange}: Quotes) =>
do this:
type Quotes = {
count: number;
onChange: ChangeEventHandler<HTMLInputElement>;
onSubmit: FormEventHandler<HTMLFormElement>;
}
const Quotes = ({children, count, onSubmit, onChange}: PropsWithChildren<Quotes>) =>
^ keeps Quotes
more pure
ex: how to pass setQuote
to a child and type it? just hover over the state method's declaration and snag it from intellisense.
interesting:
type UpdateRBGColorAction {
type: 'update-rgb-color';
payload: {rbg: [number,number,number ]} // we're telling ts, only 3 numbers allowed! it enforces this like a tuple
}
hold up....if you create a global.d.ts file, you can export a type from that and it will be available in all of your files without having to import it.
However, I'm not sure you want that?
if you're not getting auto-complete in your TS, its a dead ringer that there may be a problem (as opposed to going in your browser and checking).
type HexColor = `#${string}`;
you used to be able to only interpolate strings, but now you can interpolate a variety of values
type RBGString = `rgb(${number}, ${number}, ${number})`;
const ColorFormats = 'rbg' | 'hex' | 'hsl';
type ActionTypes = `update-${ColorFormats}-color`;
^ dope
also type guards using is
:
type HexColor = `#${string}`;
const isHexColor = (s: string): s is HexColor {
return s.startsWith('#');
}
^ you specifically tell TS "YES this is a HexColor type"
really useful in api responses, when you dont know if somethin is an array or string, thats when you use unknown
and then type check it after as above.
type ColorContextState = {
hexColor: string;
dispatch?: Dispatch<ColorActions> //not good, we shouldnt have this as optional.In the final version, no ? is there.
}
const ColorContext = createContext<>({
hexColor: '#FFFFFF';
} as ColorContextState); //looks sloppy, but its what we've got...ex: "TS, you dunno about this, but I do"
export const ColorProvider = ({children}: PropsWithChildren) => {
return(
<ColorContext.Provider value={{ hexColor: '#BADA55' }}>
{children}
</ColorContext.Provider>
)
}
now move the useReducer functionality into context:
type ColorContextState = {
hexColor: string;
dispatch: Dispatch<ColorActions>
}
const ColorContext = createContext<ColorContextState>({
hexColor: '#FFFFFF';
} as ColorContextState);
export const ColorProvider = ({children}: PropsWithChildren) => {
const [state, dispatch ] = useReducer(colorReducer, initialState);
const hexColor = state.hexcolor;
return(
<ColorContext.Provider value={{ hexColor, dispatch }}>
{children}
</ColorContext.Provider>
)
}
figure out the type based on an object...making a type dynamically on the fly:
const value = {
items,
unpackedItems,
packedItems,
};
type ItemsStated = typeof value;
another ex:
const ['loading', 'error', 'success'] as const;
type Statuses = (typeof Status)[number];
^ i.e. use a const as a value and also a type.
Partial
<-- native TS utility method, takes all the keys in an object and makes them all optional
type Item = {
name: string;
id: string;
};
type Blah = {
update: (id: string, updates: Partial<Item>) => void;
};
which is same as:
type Item = {
name?: string;
id?: string;
};
but perhaps you dont want the id to be optional here...you NEEEd that...you can omit it from being optional:
Omit
- Omit<Partial, 'id'>
type Blah = {
update: (id: string, updates: Omit<Partial<Item>, 'id'>) => void;
};
in other words
type PartialItem = Partial<Item>; //make a new type based on this info
type WithoutId = Omit<PartialItem, 'id'>; //make a new type with the partial, but make sure the id is not optional
check out these guys: https://fem-react-typescript.vercel.app/Utility%20types.md
example
type User = {
firstName: string;
lastName: string;
age: number;
};
type ActionTypes = `update-${keyof User}`;
type Actions<T, K extends keyof T & string> = {
type: `update-${K}`;
payload: T[K];
};
type UpdateNameAction = Actions<User, 'firstName'>;
typing a linkedlist with generics
type LinkNode<T> = {
value: T;
next?: LinkNode<T>;
};
const TextNode: LinkNode<string> = {
value: 'hi',
next: {
value: 'hello',
},
};
all generics do is say,"whatever T is, it should be the same T everywhere."
ex: imagine it doesnt need to be a string, it needs to be a number or an array or whatever....this is where generics are handy...T
is generic
ex:
type LinkNode<T> = {
value: T;
next?: LinkNode<T>;
};
const createLink = <T>(value: T): LinkNode<T> => {
return { value };
};
//so now you can do:
const stringNode = createLink('yo');
const anotherNode = createLink(23);
type LabeledInputProps = ComponentPropsWithoutRef<'input'> & {
label: string;
};
const LabeledInput = (label, id, onChange, ...props): LabeledInputProps => {
//
};
^ this allows you to ...spread without getting TS barking at you. This lets you get all props for an element that you'd already have, autocomplete for it too, and is a nice shorthand for passing a prop down as well.
you also get aria attrs and plenty of other native stuff for free in TS.
^ this is super helpful when creating Design System-type components for buttons,inputs, etc.
- implement
PropsWithChildren
import {PropsWithChildren} from 'react';
type BoxProps = PropsWithChildren<{
style: React.CSSProperties
}>;
const Box = ({children, style}: BoxProps) => {
return (
//
)
}
- impleemnt
React.ComponentPropsWithoutRef
type ButtonProps = React.ComponentPropsWithoutRef<'button'>;
const Button = ({ children, onClick, type }: ButtonProps) => {
return (
<button onClick={onClick} type={type}>
{children}
</button>
);
};
- consider
unknown
? - consider template literal types (ex:
0x${string}
) and associatedis
type guards
type HexColor = `#${string}`;
const isHexColor = (s: string): s is HexColor {
return s.startsWith('#');
}
- consider
as Type
within context
type ColorContextState = {
hexColor: string;
dispatch: Dispatch<ColorActions>
}
const ColorContext = createContext<ColorContextState>({
hexColor: '#FFFFFF';
} as ColorContextState);
export const ColorProvider = ({children}: PropsWithChildren) => {
const [state, dispatch ] = useReducer(colorReducer, initialState);
const hexColor = state.hexcolor;
return(
<ColorContext.Provider value={{ hexColor, dispatch }}>
{children}
</ColorContext.Provider>
)
}
- figure out type based on an object's values:
const value = {
items,
unpackedItems,
packedItems,
};
type ItemsStated = typeof value;
const ['loading', 'error', 'success'] as const;
// type Statuses = (typeof Status)[number];
-
consider Partial, Omit, and other utilities: https://fem-react-typescript.vercel.app/Utility%20types.md
-
consider generics
- reducers
sympatico, nota bene,