React Query - Combine properties from multiple custom hooks that use useQuery with type safety - reactjs

I have an arbitrary amount of custom hooks that return the query result from useQuery. What I'm trying to do is combine the return values from those hooks into one object with this structure:
{
data,
isLoading,
isFetching,
isSuccess,
}
The data property will be an array created from combining the data properties on all the results.
This is my current code:
export function isAnyProperty(
property: keyof QueryObserverBaseResult<unknown, unknown>,
...queries: UseQueryResult<unknown, unknown>[]
) {
return queries.some((query) => query[property]);
}
export function combineQueryResults<TData extends unknown[], TQueries extends UseQueryResult<TData>[]>(
...queries: TQueries
) {
const data = queries.reduce<TData[]>((acc, query) => {
if (query.data === undefined) {
return acc;
}
return [...acc, query.data];
}, []);
return {
data,
isLoading: isAnyProperty('isLoading', ...queries),
isFetching: isAnyProperty('isFetching', ...queries),
isSuccess: isAnyProperty('isSuccess', ...queries),
};
}
I can't get TypeScript to infer the values for TData when I am using rest parameters, and I am not quite sure why. One thing to note is that I did manage to get TypeScript to infer the correct types when data was an array of UseQueryResult, just not one level down with TData.
For reference, what I get back currently from data is unknown[][]. What I am expecting to get back is of course a tuple with the data values from UseQueryResult. I thought maybe the reduce function removing all undefined values might mess with TypeScript being able to infer the correct values - so I tried it with a simple map as well, which results in data: (unknown[] | undefined)[] - so that didn't work either.
I also tried this:
export function combineQueryResults<TData extends unknown[]>(...queries: UseQueryResult<TData>[]) {
const data = queries.map((query) => query.data);
return {
data,
isLoading: isAnyProperty('isLoading', ...queries),
isFetching: isAnyProperty('isFetching', ...queries),
isSuccess: isAnyProperty('isSuccess', ...queries),
};
}
But with this code, typescript will only infer the type for the first variable passed to combineQueryResult and results in a Type 'TypeOne[]' is not assignable to type 'TypeTwo[]' error.
Looks like typescript will infer the type TData when used with UseQueryResult, but not on its own:
unknown[], Vod.ConstructedAsset[], and Group.Union[] are all TData.

I created this function to combine my queries:
const combineResults = <T extends object>(
data: UseQueryResult<T[], unknown>[]
): T[] =>
data.reduce((acc, curr) => {
if (!curr.data) return acc;
return [...acc, ...curr.data];
}, []);
//my hook
const useMyHook = (teams: TeamInfo[]) => {
const result = useQueries(
//Your queries here...
);
return {
data: combineResults<MyInterface>(result),
isLoading: result.some((query) => query.isLoading),
isFetching: result.some((query) => query.isFetching),
};
};
I hope this helps you.

Related

Type 'undefined' is not assignable to type 'Element | null'

I use a component :
const data: any[] = []
<Tiers data={data}/>
My component is built like this:
const Tiers = ({
data,
}: {
data?: any;
}) => {
console.log('data', data?.length!);
if(!data.length) return undefined;
};
export default Tiers;
I get the following message automatically :
'Tiers' cannot be used as a JSX component.
Its return type 'undefined' is not a valid JSX element.
Use
return null
instead of
return undefined
to make Tiers a valid JSX component.
You need two return statements in the component like below:
const Tiers = ({
data,
}: {
data?: any;
}) => {
console.log('data', data?.length!);
if(!data.length) return null;
return null
};
export default Tiers;
Your code has two problems,
It needs at least one condition that returns a JSX.Element(even a very simple one like Hello), if not, you probably needed a function instead.
A component cannot return undefined. If you want it to return nothing, you should return null instead of undefined.

React Redux Typescript - how to set types?

I'm having hard time setting the types for this function:
interface fetchedCountries {
mergedArray?: [];
}
export const fetchCountries = () => {
return (dispatch:(() => void)) => {
console.log(dispatch)
fetch(countryListJsonFile)
.then((response) => response.json())
.then((jsonData: fetchedCountries) => {
const array = Object.entries(jsonData)[0];
const countries = array[1].map((el: any) => {
return el._id;
}).sort();
dispatch(setCountries(countries));
})
.catch(function (err) {
console.error(err);
});
};
};
it says that setCountries expected 0 arguments, but got 1.
I've tried to follow different guides, such as this, but I couldn't make it work.
I got lost. I want here to get rid of all the any types, and give the real ones.
The store has exported:
export type RootState = ReturnType<typeof store.getState>;
export type AppState = ReturnType<typeof rootReducer>;
export type AppDispatch = typeof store.dispatch;
Here is the slice:
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
countryList: [],
};
const slice = createSlice({
name: 'countryList',
initialState,
reducers: {
setCountries(state, action) {
state.countryList = action.payload;
},
},
});
export const { setCountries } = slice.actions;
export default slice.reducer;
Can anyone please help?
There are a few issues here in your code are all related. You need to properly define:
The type of dispatch in your fetchCountries function.
The type of the payload for setCountries.
The type of your state.
The type of your API response.
Incorrect or missing types higher up in the chain can cause issues further down. For example, when you find yourself setting a type in a .map() callback like this:
array[1].map((el: any) => {
It means that the array itself (array[1]) has the wrong type. So let's figure out where it's going wrong.
1. Dispatch Type
#T.D. Stoneheart is correct. The expected 0 arguments, but got 1 error comes from calling dispatch(...), not from calling setCountries(...).
Your definition dispatch:(() => void) says that dispatch does not take any arguments. This is obviously incorrect.
The good news is that you already have the correct type elsewhere in your code. It is the AppDispatch which you exported from your store file.
export const fetchCountries = () => {
return (dispatch: AppDispatch) => {
This fix is enough to resolve all red-underlined errors. But there are some other mistakes and omissions that you may want to fix.
2. Payload Type
If you don't explicitly set the type for setCountries(state, action), then your payload type becomes any. This is fine, but not ideal. It says that "anything goes" which can make it hard to see genuine problems.
To assign the correct type to the action, import the PayloadAction utility type for redux toolkit:
import { createSlice, PayloadAction } from '#reduxjs/toolkit';
And use it with the type of your payload, which is an array of countries. Looking at your thunk, these seem to be string?:
reducers: {
setCountries(state, action: PayloadAction<string[]>) {
state.countryList = action.payload;
},
},
3. State Type
Remember how I said that any can hide genuine problems? If you followed step 2 then you should be seeing one of those now.
The assignment of state.countryList = action.payload; is giving an error:
Type 'string[]' is not assignable to type 'never[]'.
Your state.countryList has type never[] because it had an initial value of [] and that's all that TypeScript knows. It doesn't know that this is supposed to be an array of country ids. You can fix that by assigning a more accurate type to your `initialState.
Either do this:
const initialState = {
countryList: [] as string[],
};
Or this:
interface SliceState { // can name it whatever
countryList: string[];
}
const initialState: SliceState = {
countryList: [],
};
Having a correct type here will make it much, much easier to use data that you've selected from your state because now RootState has the correct type for the countryList property.
4. API Response Type
interface fetchedCountries {
mergedArray?: [];
}
This type is saying that the JSON from your response is an object which maybe has a property mergedArray which is an empty array. That's it.
I'm not sure what the actual data looks like, but perhaps something like this?
interface Country {
_id: string;
}
interface FetchedCountries {
mergedArray: Country[];
}
So now you don't need to use (el: any) because TypeScript already knows that el is a Country object.
.then((jsonData: FetchedCountries) => {
const countries = jsonData.mergedArray
.map(el => el._id)
.sort();
dispatch(setCountries(countries));
})
TypeScript Playground Link
Actually, the problem lies in the parameter type of the returning function.
You returned (dispatch: ((/* no parameters */) => void)) => {} but you called dispatch(setCountries(countries)) which has one argument setCountries(countries). Setting correct number of parameters will fix, like return (dispatch: ((something: unknown) => void)) => {}.

Is there a simple idiomatic way of updating key-value states in Redux reducers

If a redux app has a slice of state containing a key-value storage of objects, the correct pattern for updating it looks like this:
return {
...state,
[id]: {
...state[id],
valueIWantToChange: 'someValue',
},
}
This is due to the immutability of redux and the need to copy all the data.
For nested reducers of known keys, using combineReducers allows one to specify nested bits of state which have their own isolated code, simplifying it.
There is no way that I know to simplify writing reducers for a key-value object though. In a state like the example above, every single reducer will need to have the boilerplate code of using spread operators to copy the state, and then the specific id we want to update, and then the state inside that id, and then finally we can update a value in our state.
I find this very tedious to write, and the resulting code unnecessarily bloated, especially when compared with mutable frameworks like Vue where one would just write state[id].valueIWantToChange = 'someValue';
Is there any existing solution or other way of making this type of reducer simpler to write?
You are probably looking for the official Redux Toolkit, which is the official recommended way of writing any Redux logic (for two years now) and brings a createReducer and createSlice api that allow you to just write mutating statements inside those reducers.
createSlice also auto-generates action creators and makes type string constants obsolete.
Such a "slice" would look like this:
const initialState = []
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
todoAdded(state, action) {
// ✅ This "mutating" code is okay inside of createSlice!
state.push(action.payload)
},
todoToggled(state, action) {
const todo = state.find(todo => todo.id === action.payload)
todo.completed = !todo.completed
},
todosLoading(state, action) {
// you can still use old-style immutable updates if you want to
return {
...state,
status: 'loading'
}
}
}
})
// autogenerated action creators
export const { todoAdded, todoToggled, todosLoading } = todosSlice.actions
export default todosSlice.reducer
For a quick overview, take a look at the last chapter of the "Redux Fundamentals" tutorial
For a deeper introduction, read the full "Redux Essentials" tutorial
For a lighter solution if you don't want to add dependencies (which some projects can't afford), I chose to implement simple helpers that only take a few lines of code. This article helped me get a food starting point for simplifying everything.
Here is some example TypeScript code you can put in a helper file and then use to write actions/reducers much more succintly without adding heavy dependencies.
import { AnyAction } from 'redux'
// Helpers for creating actions, can use version with payload if all your actions have a payload parameter, or a more generic one
export type ActionFunction = (payload: any) => AnyAction
export type FunctionWithString<T extends Function> = T & string
export interface ActionWithPayload<T = any> extends AnyAction {
payload: T
}
export type ActionCreator<T = any> = FunctionWithString<(payload: T) => ActionWithPayload<T>>
export function createPayloadAction<T>(name: string): ActionCreator<T> {
const actionCreator = (payload: T) => ({
type: name,
payload,
})
actionCreator.toString = () => name
return (actionCreator as any) as ActionCreator<T>
}
export function createAction<T>(name: string, actionFunction: ActionFunction): ActionCreator<T> {
const actionCreator = (payload: T) => ({
type: name,
payload: actionFunction(payload),
})
actionCreator.toString = () => name
return (actionCreator as any) as ActionCreator<T>
}
// The shape of your reducer functions, makes it easier to deal with having all reducers deal with the same payload-based action format
export type MyReducer<S, A = any> = (state: S, payload: A) => S
export interface ReducersMap<S> {
[key: string]: MyReducer<S>
}
export function createReducer<S>(reducers: ReducersMap<S>, defaultState: S) {
return (state: S, action: ActionWithPayload) => {
if (!state) {
return defaultState
}
const reducer = reducers[action.type]
if (reducer) {
return reducer(state, action.payload)
}
return state
}
}
// Common use case helpers for reducers
type KeyValueState<T> = { [key: string]: T }
// Updates a specific value key in a reducer that is shaped like a key-value object
export function keyValueUpdateReducer<T>(
state: KeyValueState<T>,
id: string,
value: Partial<T>,
): KeyValueState<T> {
return {
...state,
[id]: {
...state[id],
...value,
},
}
}
// Update a specific value in a reducer that is shaped like a key-value object
export function keyValueSetReducer<T>(
state: KeyValueState<T>,
id: string,
value: T,
): KeyValueState<T> {
return {
...state,
[id]: value,
}
}
Example use:
action
import { createPayloadAction } from './helpers'
export interface SetAThingPayload {
id: string
thing: any
}
export const setAThing = createPayloadAction<SetAThingPayload('SET_A_THING')
reducer
import { createReducer, keyValueSetReducer, keyValueUpdateReducer } from './helpers'
import { setAThing } from './actions'
export const thingsReducer = createReducer<MyAppState>(
{
[setAThing]: (state, { id, thing }) => keyValueSetReducer(state, id, thing),
},
{},
)

Property does not exist on type within Redux reducer using TypeScript?

I'm trying to use Redux with TypeScript in a little learning project, following this tutorial:
https://redux.js.org/recipes/usage-with-typescript/
const INITIAL_STATE: TweetsState = {
tweets: []
};
const tweetsReducer: Reducer<TweetsState> = (
state = INITIAL_STATE,
action: TweetAction
): TweetsState => {
switch (action.type) {
case GET_TWEETS:
return {
...state
};
case TWEETS_RECEIVED:
return {
tweets: [...state.tweets, action.payload]
};
default:
return state;
}
};
My reducer expects an action of type TweetAction, which is the union of two interfaces:
export interface IGetTweetsAction {
type: typeof GET_TWEETS;
}
export interface ITweetsReceivedAction {
type: typeof TWEETS_RECEIVED;
payload: ITweet[];
}
export const GET_TWEETS: string = "GET_TWEETS";
export const TWEETS_RECEIVED: string = "TWEETS_RECEIVED";
export type TweetAction = IGetTweetsAction | ITweetsReceivedAction;
However, within my reducer, I get the error Property 'payload' does not exist on type 'TweetAction' which is confusing because it suggests the union hasn't worked?
But this is further confusing because within my actions file it clearly allows me to return an object, of type TweetAction, with a payload:
import {
TweetAction,
GET_TWEETS,
TWEETS_RECEIVED,
ITweet
} from "./tweets.types";
export const getTweets = (): TweetAction => ({
type: GET_TWEETS
});
export const tweetsReceived = (tweets: ITweet[]): TweetAction => ({
type: TWEETS_RECEIVED,
payload: tweets
});
I'm aware the code is currently a bit of a mess (I sometimes instinctively prefix interfaces with I being a C# dev, sorry!) as I haven't been through a refactory stage yet as I'm just trying to whip something up quickly, but I've been stumped by this error.
Can anyone see what's going wrong? It's probably something straight forward but the isolation might be getting to my head.
Thanks!
Edit: Note that removing the typesafety and changing:
action: TweetAction to just action allows me to do action.payload and the functionality works fine...
-- edit -----
Ok So apparently you need to remove the : string typing in those lines:
export const GET_TWEETS: string = "GET_TWEETS";
export const TWEETS_RECEIVED: string = "TWEETS_RECEIVED";
-- /edit -----
I'm pretty new to typescript too, but as I understand it, since you use action.payload in your reducer, the type TweetAction is not accepted since it could imply a IGetTweetsAction type, which does not define a payload property.
You need to use a more generic type like:
type TweetAction {
type: // ...,
}
to say that you can have an action that will always have a type and might have some other props (such as a payload).
As for your confusing example, I think that actually makes sense:
export const getTweets = (): TweetAction => ({
type: GET_TWEETS
});
No payload is defined so it matches IGetTweetsAction.
export const tweetsReceived = (tweets: ITweet[]): TweetAction => ({
type: TWEETS_RECEIVED,
payload: tweets
});
While this one matches ITweetsReceivedAction.

How to write type definitions for React HoCs

I have a higher order component that deals with Firestore data for me. I'm pretty new to typescript and I'm having trouble getting the types to work as I'd like them to.
Here are the full files + some extra ts definitions
I have a couple problems:
React.Component not inferring type definitions:
type WithFirestoreHoC<Props = {}> = (
config: WithFirestoreConfig<Props>,
) => (
WrappedComponent: ComponentType<WithFirestore & Props>,
) => ComponentClass<Props, { error: Error; queries: {}; loaded: boolean }>;
const withFirestore: WithFirestoreHoC = ({
queries,
props: propPickList,
loading: { delay = 200, timeout = 0 } = {},
}) => WrappedComponent =>
class WithFirestoreConnect extends Component { ... }
config and WrappedComponent are getting their type definitions (as WithFirestoreConfig + ComponentType<WithFirestore & Props>, respectively.
However, WithFirestoreConnect is not inferring that it should be ComponentClass<Props, { error: Error; queries: {}; loaded: boolean }>.
I wouldn't mind defining the state twice, but that doesn't help me with getting Props from type WithFirestoreHoC<Props = {}> to class WithFirestoreConnect extends Component<Props, { error: Error; queries: {}; loaded: boolean }> { ... } because it can't find Props.
How to create a dynamic pick list
Part of WithFirestoreConfig defines that the config object has a list of props that get passed on to WrappedComponent
WrappedComponent: ComponentType<WithFirestore & Props>,
should really be
WrappedComponent: ComponentType<WithFirestore & Pick<Props, config.propsPickList>,
Is there a way to tell typescript that what you provide in config.propsPickList will determine what props WrappedComponent should expect?
Inferring Firestore types
There are 2 types of Firestore query responses, those for Documents and those for Collections/Queries. It would be amazing if those could be defined in config.queries as something like this:
{ queries: {
docQuery: myDocument as DocumentReference<docDataType>,
collectionQuery: myDocument as CollectionReference<docDataType>,
} }
so WrappedComponent could know whether to expect a query or document data structure on the other end.
This seems super complex so I have a simpler example (it's a shortcut that creates a single subscription) here that would at least be a good stepping stone towards what I want:
export const withFirestoreDocument: <
DataType = firestore.DocumentData,
Props = {}
>(
query: FirestoreQueryable<DataType>,
) => (
WrappedComponent: ComponentType<DocumentSnapshotExpanded<DataType>>,
) => WithFirestoreHoC<Props> = query => WrappedComponent =>
withFirestore({ queries: { _default: query } })(
mapProps<
DocumentSnapshotExpanded<DataType> & Props,
{ _default: DocumentSnapshotExpanded<DataType> } & Props
>(({ _default, ...props }) => ({ ...props, ..._default }))(WrappedComponent),
);
However I'm stuck here because I can't get mapProp's type definitions to pull from the function's type defs... What's the right way to do this?
React.Component not inferring type definitions: Make Props a type parameter of the function instead of the type alias and then declare it when you define withFirestore.
How to create a dynamic pick list: Add a PL type parameter for the union of the elements of the pick list. This will do the right thing when you let TypeScript infer PL at a call site, though it's possible for callers to produce unsound behavior by specifying PL to be a union type including elements that are not in the actual list.
Inferring Firestore types: I'm not sure where you were going with withFirestoreDocument. You can do this with another Q type parameter and some mapped types and conditional types to generate the types of the injected props from Q.
Here is my revision of withFirestore.tsx with all the new features, some unrelated fixes to get it to compile in my environment, and an example added at the bottom (which should probably rather be in a separate file):
import * as React from 'react';
import { Component, ComponentClass, ComponentType } from 'react';
import {
DocumentReference,
Query,
CollectionReference,
DocumentSnapshotExpanded,
QuerySnapshotExpanded
} from './firemodel';
import { firestore } from 'firebase';
import { pick, forEach, isEqual, isFunction } from 'lodash';
import { expandDocSnapshot, expandQuerySnapshot } from 'modules/providers/util';
import SmartLoader from 'modules/atoms/SmartLoader';
type FirestoreQueryable<DataType> =
| DocumentReference<DataType>
| Query<DataType>
| CollectionReference<DataType>;
type FirestoryQueryableFunction<
DataType,
Props
> = (
firestore: firestore.Firestore,
props: Props,
) => Promise<FirestoreQueryable<DataType>>;
type QueryConfigEntry<Props> =
FirestoreQueryable<any> | FirestoryQueryableFunction<any, Props>;
type QueryConfig<Props> = {
[queryName: string]: QueryConfigEntry<Props>;
};
type FirestoreQueryableExpanded<Props, QE extends QueryConfigEntry<Props>> =
QE extends FirestoreQueryable<any> ? FirestoreQueryableExpanded1<QE> :
QE extends FirestoryQueryableFunction<any, Props> ? FirestoreQueryableExpanded1<ReturnType<QE>> : unknown;
type FirestoreQueryableExpanded1<QE extends FirestoreQueryable<any>> =
QE extends CollectionReference<infer DataType> | Query<infer DataType> ? QuerySnapshotExpanded<DataType> :
QE extends DocumentReference<infer DataType> ? DocumentSnapshotExpanded<DataType> : unknown;
interface WithFirestoreConfig<Props, PL extends keyof Props, Q extends QueryConfig<Props>> {
/** Object containing the queries to be provided to WrappedComponent.
* The queryName used is also the prop name the snapshot is passed in. */
queries: Q;
/** A list of props to whitelist passing to WrappedComponent.
* Configs without a list will whitelist all props */
props?: PL[];
/** Loading config items */
loading?: {
/** Number of ms after which to display the loading icon */
delay?: number;
/** Number of ms after which to display the timeout message */
timeout?: number;
};
}
type WithFirestoreHoC = <Props>() => <PL extends keyof Props, Q extends QueryConfig<Props>>(
config: WithFirestoreConfig<Props, PL, Q>,
) => (
WrappedComponent: ComponentType<WithFirestore<Props, Q> & Pick<Props, PL>>,
) => ComponentClass<Props, { error: Error; queries: {}; loaded: boolean }>;
const withFirestore: WithFirestoreHoC =
// An extra function call is needed so that callers can specify Props and
// still have PL and Q inferred. It can be removed when
// https://github.com/Microsoft/TypeScript/issues/10571 is implemented.
<Props extends {}>() =>
// Note: if `props` is not passed, there will be no inference for PL and it
// will default to its constraint, which is exactly the behavior we want as
// far as typing is concerned.
<PL extends keyof Props, Q extends QueryConfig<Props>>({
queries,
props: propPickList,
loading: { delay = 200, timeout = 0 } = {},
}: WithFirestoreConfig<Props, PL, Q>) => WrappedComponent =>
class WithFirestoreConnect extends Component<Props, { error: Error; queries: WithFirestore<Props, Q>; loaded: boolean }> {
subscriptions: {
[queryName: string]: ReturnType<FirestoreQueryable<any>['onSnapshot']>;
} = {};
state = {
error: null as Error,
queries: {} as WithFirestore<Props, Q>,
loaded: false,
};
componentDidMount() {
this.restartSubscription();
}
cancelSubscriptions = () => {
forEach(this.subscriptions, unsubscribe => unsubscribe());
this.subscriptions = {};
};
restartSubscription = () => {
// Open questions:
// - figuring out when all loaded (use a promise?)
this.cancelSubscriptions();
forEach(queries, async (q: QueryConfigEntry<Props>, key) => {
let ref: FirestoreQueryable<any>;
if (isFunction(q)) {
// The fact that this is an async/await means that we can
// create dependent queries within our FirestoreQueryableFunction
ref = await q(firestore(), this.props);
} else {
// Narrowing is not working for some reason.
ref = q as FirestoreQueryable<any>;
}
if (ref instanceof firestore.DocumentReference) {
this.subscriptions[key] = ref.onSnapshot(
snap => {
this.setState({
queries: Object.assign({}, this.state.queries, {[key]: expandDocSnapshot(snap)}),
});
},
err => {
console.error(JSON.stringify(err));
this.setState({ error: err });
this.cancelSubscriptions();
},
);
} else if (
ref instanceof firestore.CollectionReference ||
ref instanceof firestore.Query
) {
let ref2: {onSnapshot(os: (snap: firestore.QuerySnapshot) => void, oe: (err: Error) => void): () => void; } = ref;
this.subscriptions[key] = ref2.onSnapshot(
snap => {
this.setState({
queries: Object.assign({}, this.state.queries, {[key]: expandQuerySnapshot(snap)}),
});
},
err => {
console.error(JSON.stringify(err));
this.setState({ error: err });
this.cancelSubscriptions();
},
);
}
});
};
componentDidUpdate(prevProps: Props) {
if (!isEqual(this.props, prevProps)) {
this.restartSubscription();
}
}
componentWillUnmount() {
this.cancelSubscriptions();
}
render() {
if (!this.state.loaded || this.state.error) {
return (
<SmartLoader
error={this.state.error}
timeout={timeout}
delay={delay}
/>
);
}
const whitelistedProps = propPickList
? pick(this.props, propPickList)
: this.props;
// Unsure what's wrong here ~ Matt
let WrappedComponent2 = WrappedComponent as any;
return <WrappedComponent2 {...whitelistedProps} {...this.state.queries} />;
}
};
export type WithFirestore<Props, Q extends QueryConfig<Props>> = {
[queryName in keyof Q]: FirestoreQueryableExpanded<Props, Q[queryName]>;
}
export default withFirestore;
// EXAMPLE
interface MyDoc {
y: number
}
declare let myDocRef: DocumentReference<MyDoc>;
declare let myCollRef: CollectionReference<MyDoc>;
let wrapped = withFirestore<{x: string}>()({
queries: {
myDoc: myDocRef,
myColl: myCollRef
},
})((props) => { return <>{props.myDoc.data.y + props.myColl.docs[props.x].data.y}</>; });

Resources