Home > OS >  How can I avoid optional chaining in Typescript?
How can I avoid optional chaining in Typescript?

Time:01-03

I'm trying to store object data called Targetfarms in redux. I assigned a type called Farmstype to Targetfarms.

However, when I bring Targetfarms with useSelector in the MainPage component and I want to use targetfarm.aircon, if I do not use optional chaining in Targetfarms, this error occurs.

Targetfarms is possibly null,

If I use optional chaining, I can get Targetfarms data normally, but I don't want to use optional chaining as much as possible.

How can I fix my code?

This is my code:

export interface Farmstype {
    aircon: aircontype;
    children: Farmstype[];
    equips: euiptype[];
    latitude: number;
    longitude: number;
    modDate: string;
    name: string;
    placeId: number;
    placeInfo: placeInfotype;
    productCode: string;
    productCodeInfo: productCodeInfotypes;
    productLifeCycles: productLifeCyclestype[];
    roadNameAddress: string;
    stnIds: number;
    type: string;
}

interface InitialState {
    Targetfarms: Farmstype | null;
}

const initialState: InitialState = {
    Targetfarms: null,
};



const postSlice = createSlice({
    name: "post",
    initialState,
    reducers: {
        // targetLoadFarm
        targetFarm(state, action: PayloadAction<Farmstype>) {
            state.Targetfarms = action.payload;
        },
    }

MainPage

const MainPage = () => {
    const { Targetfarms} = useSelector((state: RootState) => state.post);

    console.log("Targetfarms:", Targetfarms?.placeId);    // Targetfarms is possibly null,

}

CodePudding user response:

Maybe that you are trying to access a property of an object that may be null or undefined before the initialization. To avoid this error, you have a few options:

You can check if the object is null or undefined before trying to access its properties. For example:

if (Targetfarms) {
  console.log(Targetfarms.placeId);
}

You can set a default value for Targetfarms using the || operator. For example:

const {Targetfarms} = useSelector((state: RootState) => state.post) || {};
console.log(Targetfarms.placeId);

This will set the default value of Targetfarms to an empty object, which means you can access its properties without getting an error.

Note: I think and it's only my opinion here, optional chaining it's a good approach to avoid null or undefined.

CodePudding user response:

An idea to get rid of optional chaining with you current setup would be to statically type the state of the world and write different components.

Not sure I structured your store correctly but you get the idea.

type RootState = {
    app: 'init' | 'running' // we model the state of the app
    post: {
       Targetfarms: Farmstype | null
    }
}

// We dispatch to the correct implementation depending on the state of the world
const MainPage = () =>
    useSelector({ app }: RootState) => app) === 'running'
        ? RunningPage()
        : InitPage();

const RunningPage = () => {
    const { Targetfarms } = useSelector((state: RunningState) => state.post);

    // OK
    const f = Targetfarms.placeId

}

const InitPage = () => {
    const { Targetfarms } = useSelector((state: InitState) => state.post);

    // @ts-expect-error: Targetfarms is possibly null
    const f = Targetfarms.placeId

}

There are many ways to produce the different states. This is an option.

// When the app is 'running' `post/Targetfarms` is unlocked
type RunningState = Unlock<RootState, ['post', 'Targetfarms']> & {
   state: 'running'
};

// Otherwise Targetfarms is still nullable
type InitState = RootState & {
   state: 'init'
};

type Unlock<State, Path extends PropertyKey[]> = unknown & {
    [K in keyof State]: Path['length'] extends 1
        ? K extends Path[0] ? NonNullable<State[K]> : State[K]
        : Unlock<
            State[K],
            Path extends [PropertyKey, ...infer U] ? U  & PropertyKey[] : never
        >
}

This solution can start to become interesting if you have few app states and lots of nullable pieces of state. Basically you take the decision of what state the app is in once at the root instead of many times where the values are needed.

  • Related