When accessing Document.find().accessibleBy() it should return Document[] - casl

I defined mongoose model using AccessibleRecordModel like that
export const Restaurant = mongoose.model<
IRestaurantDocument, AccessibleRecordModel<IRestaurantDocument>
>('Post', RestaurantSchema);
When I try to use accessibleBy with find TS is saying that find().accessibleBy() returns IRestaurantDocument instead IRestaurantDocument[], so I need to cast types. Code works correct but Typescript types are wrong
findById().accessibleBy() return IRestaurantDocument type witch is correct.
public async getAll(): Promise<IRestaurantDocument[]> {
const ability = defineAbilitiesFor(user);
return Restaurant.find().accessibleBy(ability).populate('logo').exec();
}
"mongoose": "6.4.4"
"#casl/ability": "5.4.4",
"#casl/mongoose": "6.0.0",

Related

References a sub model from parent in typescript using prisma client

I am learning prisma and I can't figure out how to use the prisma types correctly if the returned data includes a sub model.
For example, I have the following two tables
model Services {
id Int #id #default(autoincrement())
service_name String #db.VarChar(255)
description String #db.MediumText
overall_status ServiceStatus #default(OPERATIONAL)
deleted Boolean #default(false)
sub_services SubServices[]
}
model SubServices {
id Int #id #default(autoincrement())
name String #db.VarChar(255)
description String #db.MediumText
current_status ServiceStatus #default(OPERATIONAL)
service Services? #relation(fields: [service_id], references: [id], onDelete: Cascade)
service_id Int?
}
I am then pulling data from the Services model using the following:
const services = await prisma.services.findMany({
where: {
deleted: false
},
include: {
sub_services: true
}
});
I am then in the client side referencing the Services model, but the IDE isn't detecting that Services can include sub_services. I can use it and it works but the IDE is always showing a squiggly line as if the code is wrong, example is below:
import {Services} from "#prisma/client";
const MyComponent : React.FC<{service: Services}> = ({services}) => {
return (
<>
service.sub_services.map(service => {
})
</>
)
}
but in the above example sub_services is underlined with the error TS2339: Property 'sub_services' does not exist on type 'Services'.
So how would I type it in a way that IDE can see that I can access sub_services from within services model.
UPDATE
I found a way to do it, but I'm not sure if this is the correct way or not as I am creating a new type as below:
type ServiceWithSubServices <Services> = Partial<Services> & {
sub_services: SubServices[]
}
and then change the const definition to the below
const ServiceParent : React.FC<{service: ServiceWithSubServices<Services>}> = ({service}) => {
Although this does seem to work, is this the right way to do it, or is there some more prisma specific that can do it without creating a new type.
In Prisma, by default only the scalar fields are included in the generated type. So, in your case for the Services type, all the scalar fields except sub_services would be included in the type. sub_services is not included because it's a relation field.
To include the relation fields, you would need to use Prisma.validator, here's a guide on generating types that include the relation field.

zod (or toZod): how to model "type" field in discriminated union

I have this type:
export interface ConnectorForModel {
_type: "connector.for.model",
connectorDefinitionId: string
}
I want to model is as a zod schema. Actually I am using toZod, like this:
export const ConnectorForModelZod: toZod<ConnectorForModel> = z.object({
_type: z.literal("connector.for.model"),
connectorDefinitionId: z.string()
});
And I get this type error:
Type 'ZodLiteral<"connector.for.model">' is not assignable to type 'never'.
Whats the right way to express this?
I think the quickest way to get this working is with ZodType:
import { z } from "zod";
export interface ConnectorForModel {
_type: "connector.for.model";
connectorDefinitionId: string;
}
export const ConnectorForModelZod: z.ZodType<ConnectorForModel> = z.object({
_type: z.literal("connector.for.model"),
connectorDefinitionId: z.string()
});
Aside: I tend to define my types from my zod schemas rather than the other way around. If you don't have control over the type that you're working with then the given approach is the way to go, but you could potentially avoid writing the same code twice using z.TypeOf on the zod schema
type ConnectorForModel = z.TypeOf<typeof ConnectorForModelZod>;
This type would be equivalent to your interface.

How to call endpoint.select() in RTK query with an argument to retrieve cached data (within another selector)?

I have an endpoint which accepts a parameter and I'm trying to access the cached data using endpoint.select() in a redux slice. The problem is i cant figure out how to pass in the cache key. I've done the following:
export const selectProductsResult = (storeId) =>
storeApi.endpoints.listProductsByStore.select(storeId);
This works fine if I use it within a component like this:
const currentStoreProducts = useSelector(selectProductResult(currentStoreId))
What I don't understand is how I can use this in another selector, for example this does not work:
const selectCurrentProducts = createSelector((selectCurrentStoreId), currentStoreId
=> selectProductResult(currentStoreId)
If I try to use this in a component like so:
const currentProducts = useSelector(selectCurrentProducts)
The value obtained is a memoized function. I've played around quite a bit and can't seem to build the desired selector.
The call to someEndpoint.select() generates a new selector function that is specialized to look up the data for that cache key. Loosely put, imagine it looks like this:
const createEndpointSelector = (cacheKey) => {
return selectorForCacheKey = () => {
return state.api.queries['someEndpointName'][cacheKey];
}
}
const selectDataForPikachu = createEndpointSelector('pikachu');
So, you need to call someEndpoint.select() with the actual cache key itself, and it returns a new selector that knows how to retrieve the data for that cache key:
const selectDataForPikachu = apiSlice.endpoints.getPokemon.select('pikachu');
// later, in a component
const pikachuData = useSelector(selectDataForPikachu);

exported typed functions that return functions should NOT require the returned function to be typed to work in other modules

For example if you are using redux-actions and have created the following module definition for it:
declare module 'redux-actions' {
declare type ActionType = string
declare type Action = {
type: ActionType,
payload?: any,
error?: bool,
meta?: any,
}
declare function createAction<T>(
type: string,
payloadCreator?: (...args: Array<T>) => any,
metaCreator?: Function
): (...args: Array<T>) => Action
}
and then you use that function to return a new function like this:
export const selectProfileTab = createAction('SELECT_PROFILE_TAB', (index: number) => {
playSound()
return { index }
})
and then in another file use it incorrectly like this:
selectorProfileTab('someString')
that won't report an error. It seems to be because flow requires annotations at the "boundaries" of modules. Am I correct?
Because the following does work:
export const selectProfileTab: (index: number) => any = createAction('SELECT_PROFILE_TAB', (index: number) => {
playSound()
return { index }
})
Notice I've annotated the returned function. The following will now produce an error:
selectProfileTab('someString')
I'm just trying to get a hold of this and verify this because it's a lot of extra "boilerplate" to annotate those returned functions, especially when it calling selectProfileTab('someString') would correctly produce an error if used in the same file. It makes you think: what's the point in creating module definitions for the redux-actions package which doesn't have them yet if it doesn't provide any/much value since you have to annotate your returned functions anyway. That's quite disappointing. Am I correct in determining that it's a module "boundaries" limitation/requirement with Flow? Are there any ways to get the desired result of not having to type returned functions that you export?

Passing in redux-devtools to a redux store with middleware

How is this code processed in relation to the way it is written in the redux-devtools documentation?
https://github.com/auth0-blog/redux-auth/blob/master/index.js#L10-L12
let createStoreWithMiddleware = applyMiddleware(thunkMiddleware, api)(createStore)
let store = createStoreWithMiddleware(quotesApp)
I'm not sure how to rewrite this to include DevTools but I did find this GitHub link including a pull request to include DevTools, which I've since gotten working. However, I still do not understand how it is being applied and what's going on with the let something = function(param1,param2)(function). I know that with that syntax the return value of applyMiddleware is being sent to createStore, but the createStore syntax takes a reducer, initialState, and an enhancer. How is this being applied here?
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'
const DevTools = createDevTools(
<DockMonitor toggleVisibilityKey="ctrl-h" changePositionKey="ctrl-q">
<LogMonitor theme="tomorrow" preserveScrollTop={false} />
</DockMonitor>
)
let createStoreWithMiddleware = applyMiddleware(thunkMiddleware, api)(createStore)
let store = createStoreWithMiddleware(quotesApp, DevTools.instrument())
The syntax confuses me as opposed to the following syntax from the redux-devtools documentation.
What happens to initialState? In the example there is no reference to initialState anywhere.
The store enhancer definition signature looks roughly like this (snipped from the definition of `applyMiddleware):
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
// snip actual enhancer logic
return {
...store,
dispatch
}
}
}
So, the enhancer definition actually returns a function that takes a reference to the createStore function itself.
Unfortunately, somehow people seem to have copied that very functional-oriented calling pattern from somewhere, which is really hard to understand. Not sure if it was in an earlier version of the docs, or what. Note that that particular usage pattern doesn't allow defining initialState (or, as it's about to be renamed, preloadedState).
So yes, the current definition pattern, and the one that I think is much more readable, is:
const middlewares = [thunk, myMiddleware];
const middlewareEnhancer = applyMiddleware(...middlewares);
const enhancers = compose(middlewareEnhancer, someOtherEnhancer);
const store = createStore(reducer, preloadedState, enhancers);

Categories

Resources