-
-
Notifications
You must be signed in to change notification settings - Fork 762
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
Typescript error rest client custom method #2775
Comments
The service types need to be expanded as noted in https://dove.feathersjs.com/api/client/rest.html#custom-methods with import { CustomMethod } from '@feathersjs/feathers'
type ServiceTypes = {
myservice: ClientService<...> & {
myCustomMethod: CustomMethod<CustomMethodData, CustomMethodResponse>
}
} |
I do have that configured. Apologies for not including that in the original post, here is the relevant code
|
What does the full client definition look like? I tested it out with an app generated by #2772 (which will be available shortly as |
Had to add |
I'm not sure if I misunderstood the docs or if it's a mistake But in the example it says Which I had implemented like on my client
This caused the TS error, changing line 10 from
|
Your question has prompted me to add proper client side tests to a generated app in #2788. The setup that is now working looks like this in import { feathers } from '@feathersjs/feathers'
import type { Custom, CustomData, CustomQuery } from './services/customized/customized'
export type { Custom, CustomData, CustomQuery }
import type { Message, MessageData, MessageQuery } from './services/messages/messages'
export type { Message, MessageData, MessageQuery }
import type { Testing, TestingData, TestingQuery } from './services/path/to/test/test'
export type { Testing, TestingData, TestingQuery }
import type { User, UserData, UserQuery } from './services/users/users'
export type { User, UserData, UserQuery }
import type { Paginated, ClientService, TransportConnection, Params } from '@feathersjs/feathers'
export interface ServiceTypes {
customized: ClientService<
Custom,
CustomData,
Partial<CustomData>,
Paginated<Custom>,
Params<CustomQuery>
> & {
// Add custom methods here
}
messages: ClientService<
Message,
MessageData,
Partial<MessageData>,
Paginated<Message>,
Params<MessageQuery>
> & {
// Add custom methods here
customMethod(data: any, params?: Params): Promise<any>
}
'path/to/test': ClientService<
Testing,
TestingData,
Partial<TestingData>,
Paginated<Testing>,
Params<TestingQuery>
> & {
// Add custom methods here
}
users: ClientService<User, UserData, Partial<UserData>, Paginated<User>, Params<UserQuery>> & {
// Add custom methods here
}
//
}
export const createClient = <Configuration = any>(connection: TransportConnection<ServiceTypes>) => {
const client = feathers<ServiceTypes, Configuration>()
client.configure(connection)
client.use('users', connection.service('users'), {
// List all standard and custom methods
methods: ['find', 'get', 'create', 'update', 'patch', 'remove']
})
client.use('path/to/test', connection.service('path/to/test'), {
// List all standard and custom methods
methods: ['find', 'get', 'create', 'update', 'patch', 'remove']
})
client.use('messages', connection.service('messages'), {
// List all standard and custom methods
methods: ['find', 'get', 'create', 'update', 'patch', 'remove', 'customMethod']
})
client.use('customized', connection.service('customized'), {
// List all standard and custom methods
methods: ['find', 'get', 'create', 'update', 'patch', 'remove']
})
return client
} And is used in import assert from 'assert'
import axios from 'axios'
import rest from '@feathersjs/rest-client'
import authenticationClient from '@feathersjs/authentication-client'
import { app } from '../src/app'
import { createClient } from '../src/client'
import type { UserData } from '../src/client'
const port = app.get('port')
const appUrl = `http://${app.get('host')}:${port}`
describe('application client tests', () => {
const client = createClient(rest(appUrl).axios(axios))
client.configure(authenticationClient())
before(async () => {
await app.listen(port)
})
after(async () => {
await app.teardown()
})
it('initialized the client', () => {
assert.ok(client)
})
it('creates and authenticates a user with email and password', async () => {
const userData: UserData = {
email: 'someone@example.com',
password: 'supersecret'
}
await client.service('users').create(userData)
const { user, accessToken } = await client.authenticate({
strategy: 'local',
...userData
})
assert.ok(accessToken, 'Created access token for user')
assert.ok(user, 'Includes user in authentication data')
assert.strictEqual(user.password, undefined, 'Password is hidden to clients')
const response = await client.service('messages').customMethod('something')
assert.ok(response)
await client.logout()
// Remove the test user on the server
await app.service('users').remove(user.id)
})
}) |
I'm actually wondering if for custom methods to keep them in sync with the actual signatures we should do something like import type { MessageService } from './services/messages/messages'
export interface ServiceTypes {
messages: ClientService<
Message,
MessageData,
Partial<MessageData>,
Paginated<Message>,
Params<MessageQuery>
> & Pick<MessageService, 'customMethod'>
} |
In fact, this totally works the way it should: import { defaultServiceMethods, feathers } from '@feathersjs/feathers'
import type { TransportConnection } from '@feathersjs/feathers'
import type { Message, MessageData, MessageQuery, MessageService } from './services/messages/messages'
export type { Message, MessageData, MessageQuery }
const defaultServiceMethods = ['find', 'get', 'create', 'update', 'patch', 'remove'] as const
const messageServiceMethods = [...defaultServiceMethods, 'customMethod'] as const
type MessageServiceMethods = typeof messageServiceMethods[number]
export interface ServiceTypes {
messages: Pick<MessageService, MessageServiceMethods>
}
export const createClient = <Configuration = any>(connection: TransportConnection<ServiceTypes>) => {
const client = feathers<ServiceTypes, Configuration>()
client.configure(connection)
client.use('messages', connection.service('messages'), {
// List all standard and custom methods
methods: messageServiceMethods as any
})
return client
} So we basically get all the actual valid, always up to date service methods (except that you could do |
One idea I had which I'm not sure if is entirely possible or feasible. I really dislike using strings to represent the custom functions. Like Would it be possible for us to avoid strings? |
Unfortunately not. The reason is that we need an actual array for the methods to initialise them dynamically on the client services. This is not possible with only types (the I still think it's nicer than the previous |
Understandable, once your PR is merged I will regenerate my services and test everything. Thanks for your help |
I was wondering if we could re-use the Thank you for your help in testing this out! |
Would something like this be reasonable, or is to confusing?
|
Steps to reproduce
Actual behavior
TS2345: Argument of type 'Partial<ServiceMethods<any, Partial<any>, Params<Query>>>' is not assignable to parameter of type 'ClientService<{ first_name?: string | undefined; last_name?: string | undefined; role?: string | undefined; email?: string | undefined; password?: string | undefined; id: string; }, { id?: string | undefined; ... 4 more ...; password: string; }, { ...; }, Paginated<...>, Params<...>> & { ...; }'. Type 'Partial<ServiceMethods<any, Partial<any>, Params<Query>>>' is not assignable to type 'ClientService<{ first_name?: string | undefined; last_name?: string | undefined; role?: string | undefined; email?: string | undefined; password?: string | undefined; id: string; }, { id?: string | undefined; ... 4 more ...; password: string; }, { ...; }, Paginated<...>, Params<...>>'. Types of property 'find' are incompatible. Type '((params?: Params<Query> | undefined) => Promise<any>) | undefined' is not assignable to type '(params?: Params<{ id?: string | { $gt?: string | undefined; $gte?: string | undefined; $lt?: string | undefined; $lte?: string | undefined; $ne?: string | undefined; $in?: string[] | undefined; $nin?: string[] | undefined; } | undefined; ... 7 more ...; $select?: DeepWritable<...>[] | undefined; }> | undefined) => ...'. Type 'undefined' is not assignable to type '(params?: Params<{ id?: string | { $gt?: string | undefined; $gte?: string | undefined; $lt?: string | undefined; $lte?: string | undefined; $ne?: string | undefined; $in?: string[] | undefined; $nin?: string[] | undefined; } | undefined; ... 7 more ...; $select?: DeepWritable<...>[] | undefined; }> | undefined) => ...'.
System configuration
Tell us about the applicable parts of your setup.
Module versions :
Everything else works, besides the Typescript error
The text was updated successfully, but these errors were encountered: