Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property 'value' does not exist on type 'SceneVariableState' when using behaviors.ActWhenVariableChanged #1016

Open
tredman opened this issue Dec 24, 2024 · 2 comments · May be fixed by #1025

Comments

@tredman
Copy link

tredman commented Dec 24, 2024

Hello! I'm trying Grafana Scenes for the first time using a new app bootstrapped by @grafana/create-plugin. I'm working through some examples and running into an error when trying to use the ActWhenVariableChanged behavior, described here. Here's my code, more or less copied directly from the the docs.

  const logWhenVariableChanges = new behaviors.ActWhenVariableChanged({
    variableName: 'serviceName',
    onChange: (variable) => {
      console.log(`serviceName value changed: ${variable.state.value}`);
    },
  });

I get the following typescript error when compiling, but the code otherwise seems to work (I see the console log messages when the variable changes as expected).

ERROR in ./src/pages/APMView/scenes.tsx:44:64
TS2339: Property 'value' does not exist on type 'SceneVariableState'.
    42 |     variableName: 'serviceName',
    43 |     onChange: (variable) => {
  > 44 |       console.log(`serviceName value changed: ${variable.state.value}`);
       |                                                                ^^^^^
    45 |     },
    46 |   });
    47 |

Found 1 error in 1513 ms.

I'm not really a JS/TS developer so I'm pretty far out of my element but it seems like either the TS config is too strict or the value property is just missing from the SceneVariableState definition.

interface SceneVariableState extends SceneObjectState {
    type: VariableType;
    name: string;
    label?: string;
    hide?: VariableHide;
    skipUrlSync?: boolean;
    loading?: boolean;
    error?: any | null;
    description?: string | null;
}

Am I doing something wrong here?

@tredman
Copy link
Author

tredman commented Dec 24, 2024

a bit hacky but since I'm using a QueryVariable here it uses an extension of MultiValueVariableState here - casting to that seems to make the compiler happy. Doesn't feel like I'm using this correctly though.

  const logWhenVariableChanges = new behaviors.ActWhenVariableChanged({
    variableName: 'serviceName',
    onChange: (variable) => {
      const state = variable.state as MultiValueVariableState;
      console.log(`serviceName value changed: ${state.value}`);
    },
  });

@gtk-grafana
Copy link
Contributor

@tredman you're not doing anything wrong, it looks like the docs are stale/incorrect here.

SceneVariableState doesn't contain a "value" property because some variable types don't have a string value, for example the AdHocFiltersVariable which contains an array of filters instead of a single value.

Asserting the type (with as MultiValueVariableState) will silence the compiler warning, but it would be a better practice to explicitly narrow the generic type in your onChange implementation, for example:

const logWhenVariableChanges = new behaviors.ActWhenVariableChanged({
  variableName: 'serviceName',
  onChange: (variable, behavior) => {
    // Example with another variable type that doesn't have a value prop
    if(variable instanceof AdHocFiltersVariable){
      // No value for some variables like AdHocFiltersVariable
      console.log('serviceName ad-hoc value changed', variable.state.filters)
    }

    // Check variable class instance at runtime to narrow the variable state type to MultiValueVariableState without type assertions that could introduce unexpected results if variable classes change.
    if(variable instanceof MultiValueVariable){
      console.log(`serviceName value changed: ${variable.state.value}`);
    }

    // But it's probably best to throw a runtime error so developers realize that this method was not built to work with variables besides MultiValueVariable
    if(!(variable instanceof MultiValueVariable)){
      throw new Error('Invalid variable type for behavior onChange method')
    }
    console.log(`serviceName value changed: ${variable.state.value}`);
  },
});

Also keep in mind that value on MultiValueVariableState is not necessarily a string, and is instead a VariableValue, which can be a string, number, boolean, CustomVariableValue, or an array of any of those, so you might need to additionally narrow that type to use string specific operations, for example:

    if(!(variable instanceof MultiValueVariable)){
      throw new Error('Invalid variable type for behavior onChange method')
    }
    if(isArray(variable.state.value)){
      console.log('serviceName array value changed: ', variable.state.value.map((v: VariableValue) => typeof v === 'string' && v.substring(0, 10)))
    }else if(typeof variable.state.value === 'string'){
      console.log('serviceName string value changed', variable.state.value.substring(0, 10))
    }else{
      throw new Error('Invalid variable type!')
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants