question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Wrong migration typings

See original GitHub issue

There’s something wrong with the MigrationManifest typings. The way it requires to define a migration is as follows:

export const migrations = {
  0: (state: PersistedState) => {
    return { ...state, activeId: state.activeId };
  },
};

However I get an error that activeId does not exist on PersistedState.

I also tried assigning state: PersistedState & State, which solves the error in the migrations file but then complains in the createMigrate function, since it accepts exclusively PersistedState.

A solution would be to make PersistedState take a subtype like this: PersistedState<State>, which will be imported from the reducer.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:23
  • Comments:13

github_iconTop GitHub Comments

11reactions
PymZoRcommented, Mar 24, 2021

Any news on this ? Looking at the code people are writing (https://github.com/search?l=TypeScript&o=desc&q=import+{+createMigrate+}+from+'redux-persist'%3B&s=indexed&type=Code) it seems that pretty much everyone went the way any => any or @ts-expect-error

7reactions
lafioscacommented, Nov 15, 2019

The relevant typings are…

createMigrate: https://github.com/rt2zz/redux-persist/blob/d7efde9115a0bd2d6a0309ac6fb1c018bf06dc30/types/createMigrate.d.ts#L14

Which takes a MigrationManifest: https://github.com/rt2zz/redux-persist/blob/d7efde9115a0bd2d6a0309ac6fb1c018bf06dc30/types/types.d.ts#L75-L77

Which is a dictionary of functions which take and return PersistedStates: https://github.com/rt2zz/redux-persist/blob/d7efde9115a0bd2d6a0309ac6fb1c018bf06dc30/types/types.d.ts#L9-L11

This seems odd to me for multiple reasons. For one, PersistedState can be an object containing a _persist field OR it can be undefined. But based on my reading of the createMigrate code, it doesn’t seem possible that the undefined will ever be passed into the MigrationManifest functions because if state is undefined, the function returns early here: https://github.com/rt2zz/redux-persist/blob/d7efde9115a0bd2d6a0309ac6fb1c018bf06dc30/src/createMigrate.js#L16-L20

So it already seems PersistedState is too broad for the definition of the input of these functions, as they should never receive undefined as input. But even if we ignore the undefined possibility, e.g. by converting the typing to this:

  interface MigrationManifest {
    [key: string]: (state: Exclude<PersistedState, undefined>) => Exclude<PersistedState, undefined>;
  }

we’ll still have the problem mentioned in the OP above.

The next reason the typing seems odd to me is that my app and migrations don’t care at all about the _persist slice of the state, which is the only thing PersistedState specifies. Why should the end user’s MigrationManifest type be concerned with that? The _persist slice is metadata used by the migration library logic, not part of the data being migrated.

The final reason that the typing seems odd, and perhaps ultimately a point that makes this difficult to type whatsoever, is that every step of the migrations by definition will be working on differently structured data. Let’s envision a simple example with 3 versions of a root state: https://gist.github.com/lafiosca/a992fa57dc2bb871108fbfc1ef1ad93e

Each of these migration steps (moving from version 0 to version 1, moving from version 1 to version 2) has very specific, explicit shape structures that we can define types for. It would be nice if the createMigrate argument typing could support this, although it seems to me that it would quickly become unwieldy to manage a generic type in an attempt to handle every version’s interface. I think the simplest approach may be to loosen up the typing, something like:

type MigrationManifestItem<T extends {} = {}, U extends {} = {}> = (state: T) => U;

interface MigrationManifest {
  [key: string]: MigrationManifestItem<any, any>;
}

An alternative approach which might give slightly more type safety would be to use a generic which expects a union type of every possible version state interface:

interface MigrationManifest<T extends {} = {}> {
  [key: string]: (state: T) => T;
}

export default function createMigrate<T>(migrations: MigrationManifest<T>, config?: MigrationConfig): PersistMigrate;

From our simple example, we’d then do this:

type MigrationState = RootStateV0 | RootStateV1 | RootStateV2;
const migrate = createMigrate<MigrationState>(migrations);

(If we don’t explicitly state the MigrationState generic parameter, TypeScript will infer RootStateV0 and complain about the other shapes.)

I’m willing to work up a PR if the library maintainers have a preference on how this should be improved. I am unsure if any of these would be considered a breaking change, although I have trouble imagining that anyone is successfully using createMigrate in TypeScript without some form of type coercion already. I’ve tested the latter approach locally and will likely use it via patch-package in my own project for now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Migrate from "typings" to "@types" with typescript@2.0.3
To migrate to using @types , I have 3 questions: 1.Should I simply remove typings from package.json and tsconfig.json ? and install @types ......
Read more >
USMT common issues - Windows Client | Microsoft Learn
Learn about common issues that you might see when you run the User State Migration Tool (USMT) 10.0 tools.
Read more >
Yoyo database migrations
Yoyo is a database schema migration tool. Migrations are written as SQL files or Python scripts that define a list of migration steps....
Read more >
TypeScript configuration - Angular
When the noImplicitAny flag is true and the TypeScript compiler cannot infer the type, it still generates the JavaScript files, but it also...
Read more >
Migrating to Percy CLI
The JS and non-JS Percy SDKs will have slightly different migration paths. This is due to @percy/agent being a direct dependency on current...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found