Tagged Unions & Loading State

type State = {
  items: Item[] | null;
  error: Error | null;
};

const initialState: State = {
  items: null,
  error: null
};

const successState: State = {
  items: response.data,
  error: null
};

const errorState: State = {
  items: null,
  error: response.error
};
if (state.items === null && state.error === null) {
  return ;
}

if (state.items !== null) {
  return ;
}

// We can assume we're in the error state now
return ;
type State = {
  status: 'unloaded' | 'loading' | 'loaded' | 'error';
  items: Item[] | null;
  error: Error | null;
};

const defaultState: State = {
  status: 'unloaded',
  items: null,
  error: null
};
type State = {
  loading: boolean;
  items: Item[] | null;
  error: Error | null;
};

const defaultState: State = {
  loading: false,
  items: null,
  error: null
};
if (state.loading) {
  return ;
}

if (state.items === null && state.error === null) {
  return ;
}

if (state.items !== null) {
  return ;
}

return ;
switch (state.status) {
  case 'unloaded':
    return ;
  case 'loading':
    return ;
  case 'loaded':
    return ;
  case 'error':
    return ;
}
type First = {
  tag: 'first';
  prop: number;
};

type Second = {
  tag: 'second';
  value: string;
}

type FirstOrSecond = First | Second;

declare function getFirstOrSecond(): FirstOrSecond;

const thing: FirstOrSecond = getFirstOrSecond();

switch (thing.tag) {
  case 'first':
    // TypeScript knows prop exists and is a number.
    // Accessing thing.value would cause an error.
    return thing.prop + 1;
  case 'second':
    // And value exists and is a string here
    return thing.value + ' came in second';
}
type First = {
  prop: number;
};

type Second = {
  value: string;
};

type FirstOrSecond = First | Second;

declare function getFirstOrSecond(): FirstOrSecond;

const thing: FirstOrSecond = getFirstOrSecond();

// TypeScript complains about Second not having a `prop` property.
if (typeof thing.prop === 'number') {
  // TypeScript does not know you have a First
  // without an explicit cast
  const prop = (thing as First).prop;
} else {
  const value = (thing as Second).value;
}
type UnloadedState = {
  status: 'unloaded';
};

type LoadingState = {
  status: 'loading';
};

type LoadedState = {
  status: 'loaded';
  items: Item[];
};

type ErrorState = {
  status: 'error';
  error: Error;
};

type State = UnloadedState | LoadingState | LoadedState | ErrorState;
type State = {
  items: Item[];
};

const defaultState: State = {
  items: []
};