Skip to content

Instantly share code, notes, and snippets.

@mathesond2
Last active February 21, 2023 02:29
Show Gist options
  • Save mathesond2/2c16b952ab46d61f22d1f0e71de7ed87 to your computer and use it in GitHub Desktop.
Save mathesond2/2c16b952ab46d61f22d1f0e71de7ed87 to your computer and use it in GitHub Desktop.
Unabridged notes from Steve Kinney's "React and Typescript, v2" Frontend Masters Course

Notes

Course notes via Steve Kinney

typing component children

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.

typing component state

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.

fetching API data

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

passing state methods to components

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).

template literal types

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.

context with TS

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.

TS utilities

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

Generics

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);

auto-complete

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.

my game plan for implementing into current project

  1. implement PropsWithChildren
import {PropsWithChildren} from 'react';

type BoxProps = PropsWithChildren<{
  style: React.CSSProperties
}>;

const Box = ({children, style}: BoxProps) => {
  return (
    //
  )
}
  1. impleemnt React.ComponentPropsWithoutRef
type ButtonProps = React.ComponentPropsWithoutRef<'button'>;

const Button = ({ children, onClick, type }: ButtonProps) => {
  return (
    <button onClick={onClick} type={type}>
      {children}
    </button>
  );
};
  1. consider unknown?
  2. consider template literal types (ex: 0x${string}) and associated is type guards
type HexColor = `#${string}`;

const isHexColor = (s: string): s is HexColor  {
  return s.startsWith('#');
}
  1. 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>
  )
}
  1. 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];
  1. consider Partial, Omit, and other utilities: https://fem-react-typescript.vercel.app/Utility%20types.md

  2. consider generics

  • reducers

sympatico, nota bene,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment