简体   繁体   中英

Retrieve the value of a List/Map the proper way with Immutable JS in TypeScript

I've been working with Immutable JS for a few months. And I really like the functionality it gives. But I've been doing something I don't like over and over again. It has to do with retrieving a value from either a List or Map.

When retrieving this value, I fist check if it even exist, when I it does, I want to interact with it further. But up until this day, I still don't know how to do this "the proper way".

I know what I'm writing could be much better because I've seen the functionalities (like fold) within a Functional Framework like fp-ts. And so I know there must be a nicer way of retrieving a value from a List/Map.

Does anyone know how?

I will add some code examples below and also a link to the source code:

Playground

import { Map, List } from 'immutable'
import { pipe } from 'fp-ts/function'
import { fold } from 'fp-ts/boolean'

// Example 1 - with Map
type Person = {
  name: string
  surname: string
  age: number
}

const persons = Map<number, Person>()
  .set(1, {name: 'Jack', surname: 'Bright', age: 25})
  .set(2, {name: 'Jane', surname: 'Bright', age: 22})
  .set(3, {name: 'Mike', surname: 'Bright', age: 21})

const someProgram = (id: number = 2) => {
  // ... Does some things

  // We need to update a user with id: 2
  if (persons.has(id)) {
    // This is where the problem is. We know that the person exists, because we're in the true clause. But still we get undefined as possible value.
    const person1 = persons.get(id) // Person | undefined

    // Now we add the ! and it works, but this is not nice nor elegant. What is the proper way of doing this (getting an element)?
    const person2 = persons.get(id)! // Person
  } else {
    console.log('Error')
  }
}

// Example 2 - With fp-ts & List
/**
 * I use fp-ts a lot lately, and even with this I get this ugly way of adding the ! at every retrieval.
 * An example with List<Person>. We want to get the first Person in the list if the list isn't empty.
 */
pipe(persons.isEmpty(), fold(
  // onFalse
  () => console.log('Error'),
  // onTrue
  () => {
    // We know that there is a user in this clause. But how do we get it properly?
    const person1 = persons.get(0) // Person | undefined
    const person2 = persons.get(0)! // Person
  }
))

This sounds like a unsolved issue of TS . It's not directly related to ImmutableJS, more a generic problem with nullable getter functions in TS.

You could rewrite the code by omitting the has check:

const person = persons.get(id);
if(person) {
  // do stuff
}

Alternatively persons.get(id, DEFAULT_PERSON) might always return a person object, but then you have to do if(person === DEFAULT_PERSON) which is even uglier than the exclamation mark

Or you disable strictNullChecks.

I've realized that we already solve this problem in the functional paradigm with the concept of Option. We can insert the potential value in an Option monad and work with the value if there is one, and safely fail when there is none.

I'll link my codesandbox, where in App.tsx I construct the problem and in App2.tsx I solve the problem. So if you're interested, have a look.

以“正确的方式”编辑使用不可变的检索数据

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM