Skip to content

Conversation

@vicrac
Copy link
Contributor

@vicrac vicrac commented Jan 7, 2020

Description

For now, loose typings for state make it unsafe to use with typescript. I've added additional type parameter to state, defining Schema for database. Consider:

interface Todo {
  description: string;
}

// create schema for the DB
interface DBSchema {
  todos: Todo;
}

const rootReducer = combineReducers({
  firebase: makeFirebaseReducer<DBSchema>()  // new API for passing type parameter
})

type AppState = ReturnType<typeof rootReducer>;

Now if we create a selector...

export const firebaseSelector = (state: AppState) => state.firebase;

export const allItemsSelector = createSelector(
  firebaseSelector,
  firebase => firebase.data.todos
);

we are getting autocompletion!:
image

... and proper inner type!
image

Of course, I am open to suggestions, but I literally made the smallest possible change that allowed me to get these features (and not to crash types).

@codecov
Copy link

codecov bot commented Jan 7, 2020

Codecov Report

Merging #826 into master will not change coverage.
The diff coverage is n/a.

@@           Coverage Diff           @@
##           master     #826   +/-   ##
=======================================
  Coverage   91.43%   91.43%           
=======================================
  Files          29       29           
  Lines         759      759           
=======================================
  Hits          694      694           
  Misses         65       65

@prescottprue
Copy link
Owner

Amazing work! Thanks for the PR! I think this is part of what what was being described in #670

Since this doesn't seem to be breaking in anyway we should be able to get this out in a new minor version

@prescottprue prescottprue changed the title Add typed db schema feat(types): add support for typed db schema Jan 7, 2020
@vicrac
Copy link
Contributor Author

vicrac commented Jan 8, 2020

That's great 😄 I gave it a bit thought and here are things that we should consider (but note it's not necesarrily to be implemented now. I think these types should be rather released as soon as possible, feedback should be gathered and then they should be gradually improved):

  1. Doing the same kind of types for firestore
  2. I realized currently my solution works only for not-nested objects, due to Record<string | number, string | number> given as type. There should be developed a generic type for any kind of data that can be stored in database (it will probably be a recursive type, sth like type DBObject = Record<string | number, string | number | DBObject>) if there's no such yet.
  3. What if schema for DB contains not only collections, but also some "unique" object (e.g. for storing denormalized counters)? I mean
{
  todos: { ... },
  counters: {
    todos: 15
  }
}

it should be reflected in schema so it doesn't produce a Record from it. I thought of sth like:

import { Collection } from 'react-redux-firebase';

interface Counters {
  todos: number;
}

interface Schema {
  todos: Collection<Todo>;
  counters: Counters;
}

and then branching on it using most probably conditional types.
4. I've also seen people doing some nesting for joins' purposes, e.g

{
  users: { 
    1: { name: "John Doe" }
  },
  todos: {
    1: {  // user's 1 todos
      2: {
        description: "Some todo"
      }
    }
  }
}

(then retrieving todos for user 1 is simpler). How should it be handled? How is it currently handled by implementation itself?

@vicrac
Copy link
Contributor Author

vicrac commented Jan 13, 2020

Guys, any conclusions on above? I'd love to have this feature in my project :)
@prescottprue

@prescottprue
Copy link
Owner

prescottprue commented Jan 21, 2020

Love the idea you are mentioning above, but agree it can come at a later time.

Going to point this to v3.1.0 and so that we can get it released - I'm thinking we will also want to add something about this to the documentation for other folks to get the same benefits.

Is the makeFirebaseReducer function necessary or have you found a way for the type to be passed with the default reducer import and assignment? Either way, it would also be nice to include an explanation of how this is working as part of the documentation

@vicrac
Copy link
Contributor Author

vicrac commented Jan 21, 2020

@prescottprue Great. Unfortunately, makeFirebaseReducer is necessary to pass type parameter - Typescript doesn't have polymorphic literals, so you can't do

combineReducers({
  firebase: firebaseReducer<DBSchema>  // <-- won't compile
})

Also, I don't really have free time to polish it nor write proper documentation, but in case of any questions regarding Typescript/anything else I'd be happy to help 👍

@prescottprue prescottprue changed the base branch from master to v3.1.0 January 21, 2020 19:32
@prescottprue prescottprue merged commit 19cc871 into prescottprue:v3.1.0 Jan 24, 2020
@prescottprue prescottprue mentioned this pull request Jan 24, 2020
3 tasks
prescottprue added a commit that referenced this pull request Jan 24, 2020
* fix(auth): remove stale profile listener on empty auth state change - @illuminist - #508, #838
* feat(types): add support for typed db schema - @vicrac - #826
* feat(auth): add reauthenticate method from firebase - @magrinj - #812
* chore(deps): bump lodash from 4.17.11 to 4.17.15 in react-native example - @dependabot
* chore(deps): bump stringstream from 0.0.5 to 0.0.6 in react-native-firebase example - @dependabot
* chore(deps): bump mixin-deep from 1.3.1 to 1.3.2 in react-native example - @dependabot
* chore(deps): bump diff in react-native-firebase example - @dependabot
* chore(deps): bump lodash-es from 4.17.4 to 4.17.15 in react-native-firebase example - @dependabot
* chore(deps): bump diff from 3.3.0 to 3.5.0 in react-native-firebase example - @dependabot

Co-authored-by: Jérémy M <[email protected]>
Co-authored-by: Michał Kaczanowicz <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: illuminist <[email protected]>
prescottprue added a commit that referenced this pull request Jan 24, 2020
* fix(auth): remove stale profile listener on empty auth state change - @illuminist - #508, #838
* feat(types): add support for typed db schema - @vicrac - #826
* feat(auth): add reauthenticate method from firebase - @magrinj - #812
* chore(deps): bump lodash from 4.17.11 to 4.17.15 in react-native example - @dependabot
* chore(deps): bump stringstream from 0.0.5 to 0.0.6 in react-native-firebase example - @dependabot
* chore(deps): bump mixin-deep from 1.3.1 to 1.3.2 in react-native example - @dependabot
* chore(deps): bump diff in react-native-firebase example - @dependabot
* chore(deps): bump lodash-es from 4.17.4 to 4.17.15 in react-native-firebase example - @dependabot
* chore(deps): bump diff from 3.3.0 to 3.5.0 in react-native-firebase example - @dependabot

Co-authored-by: Jérémy M <[email protected]>
Co-authored-by: Michał Kaczanowicz <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: illuminist <[email protected]>
@rscotten
Copy link
Contributor

rscotten commented Jan 24, 2020

@prescottprue @vicrac
I love the idea and intention here, but this PR is incomplete, and its release a little premature because it broke my typed code.

  1. It only defines a Schema for Firebase Realtime DB, not Firestore.
  2. It really needs some documentation on how to implement. Like, how do I type this?
import { combineReducers } from 'redux';
import { firestoreReducer } from 'redux-firestore';
import { firebaseReducer } from 'react-redux-firebase';

interface User { };

interface Schema { };

interface RootState {
  // previously
  firebase: FirebaseReducer.Reducer<User>;
  // change to
  firebase: FirebaseReducer.Reducer<Schema, User>;
  firestore: FirestoreReducer.Reducer;
}

combineReducers<RootState>({
    firebase: firebaseReducer,
    firestore: firestoreReducer,
  });

I admit this change is not too difficult for a Typescript developer to figure out, but documentation with this release would have saved me some debugging time.

@rscotten
Copy link
Contributor

rscotten commented Jan 24, 2020

Also, Schema should have been made the second generic parameter to FirebaseReducer.Reducer instead of replacing UserType as the first. This is going to cause everyone's code to break.

@prescottprue
Copy link
Owner

@rscotten Thanks for pointing out - I'm open to a PR if you get a chance

@w0nche0l
Copy link

w0nche0l commented Feb 6, 2020

Yeah this was definitely an annoying breaking change to figure out, please put it in the patch notes or something?

prescottprue pushed a commit that referenced this pull request Feb 6, 2020
* fix(types): make Schema the second generic parameter - #826
* chore(docs): add a typescript page to the recipes section of the docs (explains passing schema to reducer)
@prescottprue
Copy link
Owner

@w0nche0l The support for schema was mentioned in the release notes, but the break of types was not intended as part of the change. Instead of noting the workaround I am making the update mentioned by @rscotten (i.e. having Schema be the second generic type). Also, I have been working on updating the typescript example and adding a page to the recipes section of the docs explaining how to pass a schema type when using Typescript.

As for the schema handling for Firestore, I'll have to get to that when I can unless someone else wants to make a PR

@prescottprue prescottprue mentioned this pull request Feb 26, 2020
3 tasks
prescottprue added a commit that referenced this pull request Feb 27, 2020
* fix(types): add types for `onAttemptCollectionDelete` to fix `implicit any` error (#870) - @macinjoke
* fix(populate): handle firestore list type in populate (#871) - @illuminist
* fix(types): add missing `onAuthStateChanged` for config object - @illuminist
* fix(types): relax typing for `UserProfile` to be generic object - @illuminist
* fix(types): make Schema the second generic parameter (#826)
* chore(docs): add a typescript page to the recipes section of the docs (explains passing schema to reducer)

Co-authored-by: illuminist <[email protected]>
Co-authored-by: macinjoke <[email protected]>
Co-authored-by: Marko Elez <[email protected]>
Co-authored-by: Chris Dzoba <[email protected]>
}

export namespace FirebaseReducer {
export interface Reducer<ProfileType = {}> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is breaking change. The parameter order was changed.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call out. Yeah, this was in an attempt to have the feature work as originally intended

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants