Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
431 views
in Technique[技术] by (71.8m points)

typeguards - Narrowing TypeScript disjoint union with literal type guard works in function, but not on React state

I have a reducer with a nextAction state that works as sort of a callback. The NextAction is typed like so:

type NextActionSave = {type: 'save'};
type NextActionSaveAndSet = {type: 'saveAndSet', name: TextFieldName, value: string};
type NextAction = NextActionSave | NextActionSaveAndSet;
interface EditRowState {
  ...
  nextAction: NextAction | null;
}

In another file, I have a switch statement on state.nextAction.type

import React from 'react';
import {EditRowState, EditRowAction, NextAction} from './reducer';

interface Props {
  save: () => Promise<void>;
  state: EditRowState;
  dispatch: React.Dispatch<EditRowAction>;
}

const useNextAction = (props: Props) => {
  React.useEffect(() => {
    executeNextAction(props.state.nextAction);
  }, [props.state.nextAction]);

  const executeNextAction = (nextAction: NextAction | null) => {
    if (nextAction === null) return;

    switch(nextAction.type) {
      case 'save':
        props.save();
        break;
      case 'saveAndSet':
        props.save().then(() => {
          const {name, value} = nextAction;
          props.dispatch({type: 'set', name, value, advance: true});
        });
      default: return;
    }
  };
};

This works. Here is a TypeScript Playground of the executeNextAction showing the executeNextAction function.

If I move the body of executeNextAction inside the useEffect, so that nextAction comes directly from the state, it says, "Property 'name' does not exist on type 'NextAction'". I get the same error for the value property. My code without a separate executeNextAction function is below. Here is a simplified version of my code below in a TypeScript Playground showing the error.

import React from 'react';
import {EditRowState, EditRowAction} from './reducer';

interface Props {
  save: () => Promise<void>;
  state: EditRowState;
  dispatch: React.Dispatch<EditRowAction>;
}

const useNextAction = (props: Props) => {
  React.useEffect(() => {
    if (props.state.nextAction === null) return;

    switch(props.state.nextAction.type) {
      case 'save':
        props.save();
        break;
      case 'saveAndSet':
        props.save().then(() => {
          const {name, value} = props.state.nextAction;
          props.dispatch({type: 'set', name, value, advance: true});
        });
      default: return;
    }
  }, [props.state.nextAction]);
};

It works if I typecast props.state.nextAction using as NextActionSaveAndSet. It looks like the switch statement acts as a literal type guard, so I shouldn't need the type cast. I'm guessing this is why it works in the version where I moved the code into a executeNextAction function. What's the difference between using nextAction as a function parameter or accessing it on the state? Would I be able to use it on the state if I added an as const somewhere?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...