Skip to content

Latest commit

 

History

History
126 lines (97 loc) · 5.93 KB

avoid-optional.md

File metadata and controls

126 lines (97 loc) · 5.93 KB

Item 37: Limit the Use of Optional Properties

Things to Remember

  • Optional properties can prevent the type checker from finding bugs and can lead to repeated and possibly inconsistent code for filling in default values.
  • Think twice before adding an optional property to an interface. Consider whether you could make it required instead.
  • Consider creating distinct types for un-normalized input data and normalized data for use in your code.
  • Avoid a combinatorial explosion of options.

Code Samples

interface FormattedValue {
  value: number;
  units: string;
}
function formatValue(value: FormattedValue) { /* ... */ }

💻 playground


interface Hike {
  miles: number;
  hours: number;
}
function formatHike({miles, hours}: Hike) {
  const distanceDisplay = formatValue({value: miles, units: 'miles'});
  const paceDisplay = formatValue({value: miles / hours, units: 'mph'});
  return `${distanceDisplay} at ${paceDisplay}`;
}

💻 playground


type UnitSystem = 'metric' | 'imperial';
interface FormattedValue {
  value: number;
  units: string;
  /** default is imperial */
  unitSystem?: UnitSystem;
}

💻 playground


interface AppConfig {
  darkMode: boolean;
  // ... other settings ...
  /** default is imperial */
  unitSystem?: UnitSystem;
}

💻 playground


function formatHike({miles, hours}: Hike, config: AppConfig) {
  const { unitSystem } = config;
  const distanceDisplay = formatValue({
    value: miles, units: 'miles', unitSystem
  });
  const paceDisplay = formatValue({
    value: miles / hours, units: 'mph'  // forgot unitSystem, oops!
  });
  return `${distanceDisplay} at ${paceDisplay}`;
}

💻 playground


declare let config: AppConfig;
const unitSystem = config.unitSystem ?? 'imperial';

💻 playground


const unitSystem = config.unitSystem ?? 'metric';

💻 playground


interface InputAppConfig {
  darkMode: boolean;
  // ... other settings ...
  /** default is imperial */
  unitSystem?: UnitSystem;
}
interface AppConfig extends InputAppConfig {
  unitSystem: UnitSystem;  // required
}

💻 playground


function normalizeAppConfig(inputConfig: InputAppConfig): AppConfig {
  return {
    ...inputConfig,
    unitSystem: inputConfig.unitSystem ?? 'imperial',
  };
}

💻 playground