over_react_unsafe_required_prop_access

Severity: AnalysisErrorSeverity.WARNING

Maturity: experimental

Since 1.0.0

View the Project on GitHub workiva/over_react

DO NOT access required props when they're not guaranteed to be present, since that can cause errors and bad behavior.

Required props are only validated to be present on props a component was rendered with, and not on other props objects.

For example, given props:

mixin FooProps {
  late int requiredProp;
}

example() {
  final props = Foo(); // Create an empty props object.
   
  // Throws because the map is empty, and the value `null`
  // is not an `int`.
  props.requiredProp;  
}

DO use utility methods getRequiredProp, getRequiredPropOrNull, or containsProp checks to safely access the prop.

GOOD:

renderFoo([Map? _additionalFooProps]) {
  final fooProps = Foo({...?_additionalFooProps});
  
  // Safe access via `.getRequiredProp`
  final requiredProp1 = fooProps.getRequiredProp((p) => p.requiredProp1),
      orElse: () => 'custom default');
      
  // Safe access via `.getRequiredPropOrNull`
  final requiredProp2Uppercase = fooProps
      .getRequiredPropOrNull((p) => requiredProp2)
      ?.toUpperCase();
    
  // Safe access via if-check with `.containsProp`
  final otherPropsToAdd = Foo();
  if (fooProps.containsProp((p) => p.requiredProp3)) {
    otherPropsToAdd.aria.label = fooProps.requiredProp3;
  }
  
  // ...
}

BAD:

@override
renderFoo([Map? _additionalFooProps]) {
  final fooProps = Foo({...?_additionalFooProps});
  
  // Unsafe; `.requiredProp1` will throw if it's not present.
  final requiredProp1 = fooProps.requiredProp1 ?? 'custom default';
      
  // Unsafe; `.requiredProp1` will throw if it's not present.
  // Also, there's a static analysis error because there's a null-aware 
  // on the non-nullable `requiredProp2`
  final requiredProp2Uppercase = fooProps.requiredProp2?.toUpperCase();
    
  // Unsafe; `.requiredProp3` will throw if it's not present.
  // Also, there's a static analysis error on the condition because 
  // the non-nulllable requiredProp3 will always `!= null`.
  final otherPropsToAdd = Foo();
  if (fooProps.requiredProp3 != null) {
    otherPropsToAdd.aria.label = fooProps.requiredProp3;
  }
  
  // ...
}