Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(types): support providing type for firebase state #670

Open
lazarljubenovic opened this issue Apr 5, 2019 · 1 comment
Open

feat(types): support providing type for firebase state #670

lazarljubenovic opened this issue Apr 5, 2019 · 1 comment

Comments

@lazarljubenovic
Copy link

Do you want to request a feature or report a bug? Feature

(If this is a usage question, please do not post it here—post it on gitter. If this is not a “feature” or a “bug”, or the phrase “How do I...?” applies, then it's probably a usage question.)

What is the current behavior?

No type info for the schema of used database.

What is the expected behavior?

Moar typez.

Basic idea

The library provides an empty interface:

export interface FirebaseRtdbSchema { }

Developer has his own types for the schema. Not related to the lib in any way, he can do whatever he wants.

// some helpers
type RtdbUid<T> = string & { __UID_FOR__: T }
type FirebaseRecord<T> = Record<RtdbUid<T>, T>
type FirebaseKvPair<T> = { key: RtdbUid<T>, value: T }
type FirebaseKvPairs<T> = Array<FirebaseKvPair<T>>

interface RtdbUser {
  avatarUrl: string
  displayName: string
  email: string
}

interface RtdbGameScore {
  score: number
  size: number
  speed: number
  user: RtdbUid<RtdbUser>
  userDisplayName: string
}

interface RtdbSchema {
  users: FirebaseRecord<RtdbUser>
  gameScores: FirebaseRecord<RtdbGameScore>
}

Developer creates a .d.ts file where he extends the existing typings by merging in his own schema:

// make react-redux-firebase aware of the schema, globally
import 'react-redux-firebase'
declare module 'react-redux-firebase' {
  export interface FirebaseRtdbSchema extends RtdbSchema { }
}

Various methods and properties in the library can now use FirebaseRtdbSchema to bring more type safety by having a generic fnName<T extends Record<string, any> = FirebaseRtdbSchema>. If the developer doesn't provide anything, the globally declared schema is used; overriding mechanism allows for having multiple databases and such for advanced use cases.

Leveraging this means that the developer gets things like the following autocompletion.

image

Nested keys

When the lib requires specifying a path such as foo, foo.bar or foo/bar, we can provide a simple .join('/') function which has some magic types:

function chainOfKeys <
  T extends Record<string, any> = Schema,
  K1 extends keyof T = keyof T,
  K2 extends (T[K1] extends Record<string, any> ? keyof T[K1] : never) = keyof T[K1],
  K3 extends (T[K1][K2] extends Record<string, any> ?  keyof T[K1][K2] : never) = keyof T[K1][K2],
  K4 extends (T[K1][K2][K3] extends Record<string, any> ? keyof T[K1][K2][K3] : never) = keyof T[K1][K2][K3],
> (keys: [K1] | [K1, K2] | [K1, K2, K3, K4]) {
  return keys.join('.')
}

Or, we can build this idea into the existing functions which accept such path. This will allow proper type checking of nested keys.

interface Schema {
  one: string
  two: {
    two_one: string
  }
  three: {
    three_one: {
      three_one_one: {
        three_one_one_one: string
        three_one_one_two: string
      }
    }
  }
}

chainOfKeys(['one'])
chainOfKeys(['one', 'two']) // oops
chainOfKeys(['two'])
chainOfKeys(['two', 'two_one'])
chainOfKeys(['three', 'three_one', 'three_one_one', 'tree_one_one_two']) // oops (can you see it?)

Query params

Similarly, we can bring more types to the current query thingy. Strings that are being parsed later don't play well with TypeScript: we need more semantics than that. We'd have to make some sort of query builder, like the following.

const queryParams = queryBuilder.orderByChild('score').fluentApi('likeThis')

This would return some type such as Query<'score', 'likeThis'> which could be further used to check if score and likeThis are valid keys from the schema.

What about the rest?

I'm still exploring Firebase so some of these ideas might be naive because only work in very basic cases, so please tell me downsides and cases that this wouldn't cover.

Of course, this idea isn't only for Real Time Database (RTDB), but for all other similar things that Firebase has to offer.

@prescottprue
Copy link
Owner

prescottprue commented Apr 5, 2019

I really like this idea, thanks for posting! Got a laugh out of some of the wording like "more types to the current query thingy" 😆 I am a really big fan of the fact that doing the typing of all of this stuff makes the lack of naming of certain things so apparent.

@prescottprue prescottprue changed the title Providing type for the schema feat(core): support providing type for firebase state Jul 13, 2019
@prescottprue prescottprue changed the title feat(core): support providing type for firebase state feat(types): support providing type for firebase state Oct 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants