3

During learning Typescript I faced problem when I used constant variable with the value of action which I use to reducer (created to prevent typo - I have read somewhere it is good practice).

const INPUT_CHANGE: string = "INPUT_CHANGE";
const INPUT_BLUR: string = "INPUT_BLUR";

I also have created type to define reducer action:

type InputReducerAction =
  | { type: INPUT_CHANGE; value: string; isValid: boolean }
  | { type: INPUT_BLUR };

And here is my reducer:

const inputReducer = (state: InputReducerState, action: InputReducerAction) => {
  switch (action.type) {
    case INPUT_CHANGE:
      return {
        ...state,
        value: action.value,
        isValid: action.isValid,
      };
    case INPUT_BLUR:
      return {
        ...state,
        touched: true,
      };
    default:
      return state;
  }
};

When I have everythink like above INPUT_CHANGE and INPUT_BLUR in InputReducerAction are marked as error:

'INPUT_CHANGE' refers to a value, but is being used as a type here. Did you mean 'typeof INPUT_CHANGE'?ts(2749)

'INPUT_BLUR' refers to a value, but is being used as a type here. Did you mean 'typeof INPUT_BLUR'?ts(2749)

Problem disappear when I use double quotes for this constants, like so

type InputReducerAction =
  | { type: "INPUT_CHANGE"; value: string; isValid: boolean }
  | { type: "INPUT_BLUR" };

But my const variables are useless right now. What I have done wrong or missed?

During writing this question I got idea to use enum:

enum INPUT_ACTION {
  INPUT_CHANGE = "INPUT_CHANGE",
  INPUT_BLUR = "INPUT_BLUR"
}

type InputReducerAction =
  | { type: INPUT_ACTION.INPUT_CHANGE; value: string; isValid: boolean }
  | { type: INPUT_ACTION.INPUT_BLUR };

const inputReducer = (state: InputReducerState, action: InputReducerAction) => {
  switch (action.type) {
    case INPUT_ACTION.INPUT_CHANGE:
      return {
        ...state,
        value: action.value,
        isValid: action.isValid,
      };
    case INPUT_ACTION.INPUT_BLUR:
      return {
        ...state,
        touched: true,
      };
    default:
      return state;
  }
};

and using it in reducer and type. Errors disappear BUT is it good idea or can I do this in a better way

sqtr
  • 199
  • 1
  • 3
  • 12

1 Answers1

4

You could use const assertions after v3.4 to do what you are describing.

const INPUT_CHANGE = "INPUT_CHANGE" as const;
const INPUT_BLUR = "INPUT_BLUR" as const;

type InputReducerAction =
  | { type: typeof INPUT_CHANGE; value: string; isValid: boolean }
  | { type: typeof INPUT_BLUR };

const changeAction: InputReducerAction = {
  type: INPUT_CHANGE, value: '', isValid: false
};

const blurAction: InputReducerAction = {
  type: INPUT_BLUR
};

The problem is that the JavaScript variables will only have their values assigned at runtime, and TypeScript needs to know it at compile time. Using the const assertions is a way to inform the compiler that their values should not change. And when you are getting the typeof the variable, to use the literal value instead of the generic string type that is normally assigned to the variable.

thgaskell
  • 12,772
  • 5
  • 32
  • 38