Why does TypeScript not throw an error when a variable with wrong type is passed to useState?

Viewed 43

Why does the TypeScript not throw an error when a variable is passed to useState? Is there a way I can still type check when passing a variable?

type BuildingNames = [
  'farm',
  'sawmill',
];

type BuildingType = { [n in BuildingNames[number]]?: number };

interface ITownContext {
  buildings: BuildingType;
  setBuildings: (buildings: BuildingType) => void;
}

interface TownProviderProps {
  children: ReactNode;
}

const defaultValues = {
  resourceBuildings: {
    farm: 1,
    asdfasdf: 5,
  },
};

const TownContext = createContext<ITownContext>({} as ITownContext);

const TownProvider = ({ children }: TownProviderProps) => {
  // no error
  const [buildings, setBuildings] =
    useState<BuildingType>(defaultValues.buildings);
  
  // error occurs
  const [buildingsDirect, setBuildingsDirect] =
    useState<BuildingType>({ asdf: 1 });

  return (
    <TownContext.Provider
      value={{
        buildings,
        setBuildings,
      }}
    >
      {children}
    </TownContext.Provider>
  );
};

export { TownContext, TownProvider };
1 Answers

The difference you see between the 2 usages of useState is the effect of TypeScript excess property check, which triggers only when doing an assignment of an object literal (i.e. as opposed to assigning the reference of another variable, like defaultValues.buildings).

The fact that excess property check is not enforced when assigning the reference of another variable, enables a classic OOP usage of passing a subtype (here an object with more properties than strictly necessary).

Still, even when excess property check does not kick in, TS still checks for other errors: should one of the properties not match its type, it will be reported.

const defaultValues = {
  buildings: {
    farm: "string instead of number",
    asdfasdf: 5,
  },
};

useState<BuildingType>(defaultValues.buildings) // Types of property 'farm' are incompatible. Type 'string' is not assignable to type 'number'.
//                     ~~~~~~~~~~~~~~~~~~~~~~~

Playground Link

Related