From a6e1058648d82ad2a516604c1589b014b49b7061 Mon Sep 17 00:00:00 2001 From: Steven Luscher Date: Tue, 22 Aug 2023 20:25:18 +0000 Subject: [PATCH] refactor(experimental): reduce `getProgramAccounts` types using generics # Summary This _so_ close to works. You can see the types in action here: ``` const address = 'abc' as Base58EncodedAddress; /** No encoding */ const noEncodingWithContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { withContext: true, }); const noEncodingFalseContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { withContext: false, }); const noEncodingNoContextConfig = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address); /** Base58 */ const base58EncodingWithContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base58', withContext: true, }); const base58EncodingFalseContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base58', withContext: false, }); const base58EncodingNoContextConfig = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base58', }); /** Base64 */ const base64EncodingWithContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64', withContext: true, }); const base64EncodingFalseContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64', withContext: false, }); const base64EncodingNoContextConfig = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64', }); /** Base64-zstd */ const base64ZStdEncodingWithContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64+zstd', withContext: true, }); const base64ZStdEncodingFalseContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64+zstd', withContext: false, }); const base64ZStdEncodingNoContextConfig = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'base64+zstd', }); /** JSON parsed */ const jsonParsedEncodingWithContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'jsonParsed', withContext: true, }); const jsonParsedEncodingFalseContext = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'jsonParsed', withContext: false, }); const jsonParsedEncodingNoContextConfig = (null as unknown as GetProgramAccountsApi).getProgramAccounts(address, { encoding: 'jsonParsed', }); ``` There's _one_ TypeScript bug, though, that makes `rpc.getProgramAccounts()` ultimately lose all of its type information. It's because of this: https://github.com/microsoft/TypeScript/issues/55435#issuecomment-1688869763 --- .../src/rpc-methods/getProgramAccounts.ts | 119 +++++++----------- packages/rpc-transport/src/json-rpc-types.ts | 109 ++++------------ packages/rpc-transport/src/json-rpc.ts | 10 +- 3 files changed, 84 insertions(+), 154 deletions(-) diff --git a/packages/rpc-core/src/rpc-methods/getProgramAccounts.ts b/packages/rpc-core/src/rpc-methods/getProgramAccounts.ts index 53c4fdcc3657..21d496c6c70b 100644 --- a/packages/rpc-core/src/rpc-methods/getProgramAccounts.ts +++ b/packages/rpc-core/src/rpc-methods/getProgramAccounts.ts @@ -34,107 +34,84 @@ type GetProgramAccountsApiCommonConfig = Readonly<{ filters?: (GetProgramAccountsMemcmpFilter | GetProgramAccountsDatasizeFilter)[]; }>; +type GetProgramAccountsApiContextConfig = Readonly<{ + withContext?: boolean; +}>; + type GetProgramAccountsApiSliceableCommonConfig = Readonly<{ /** Limit the returned account data */ dataSlice?: DataSlice; }>; + +type GetProgramAccountsApiResponseWithContextConfig< + TContextConfig extends GetProgramAccountsApiContextConfig | void, + TResponse, +> = TContextConfig extends object + ? TContextConfig['withContext'] extends true + ? RpcResponse + : TResponse + : TResponse; + export interface GetProgramAccountsApi { /** * Returns the account information for a list of Pubkeys. */ - getProgramAccounts( + getProgramAccounts( program: Base58EncodedAddress, config: GetProgramAccountsApiCommonConfig & GetProgramAccountsApiSliceableCommonConfig & + TContextConfig & Readonly<{ encoding: 'base64'; - withContext: true; - }> - ): RpcResponse[]>; + }>, + ): GetProgramAccountsApiResponseWithContextConfig< + TContextConfig, + AccountInfoWithPubkey[] + >; - getProgramAccounts( - program: Base58EncodedAddress, - config: GetProgramAccountsApiCommonConfig & - GetProgramAccountsApiSliceableCommonConfig & - Readonly<{ - encoding: 'base64'; - withContext?: boolean; - }> - ): AccountInfoWithPubkey[]; - - getProgramAccounts( - program: Base58EncodedAddress, - config: GetProgramAccountsApiCommonConfig & - GetProgramAccountsApiSliceableCommonConfig & - Readonly<{ - encoding: 'base64+zstd'; - withContext: true; - }> - ): RpcResponse[]>; - - getProgramAccounts( + getProgramAccounts( program: Base58EncodedAddress, config: GetProgramAccountsApiCommonConfig & GetProgramAccountsApiSliceableCommonConfig & + TContextConfig & Readonly<{ encoding: 'base64+zstd'; - withContext?: boolean; - }> - ): AccountInfoWithPubkey[]; - - getProgramAccounts( - program: Base58EncodedAddress, - config: GetProgramAccountsApiCommonConfig & - Readonly<{ - encoding: 'jsonParsed'; - withContext: true; - }> - ): RpcResponse[]>; + }>, + ): GetProgramAccountsApiResponseWithContextConfig< + TContextConfig, + AccountInfoWithPubkey[] + >; - getProgramAccounts( + getProgramAccounts( program: Base58EncodedAddress, config: GetProgramAccountsApiCommonConfig & + TContextConfig & Readonly<{ encoding: 'jsonParsed'; - withContext?: boolean; - }> - ): AccountInfoWithPubkey[]; + }>, + ): GetProgramAccountsApiResponseWithContextConfig< + TContextConfig, + AccountInfoWithPubkey[] + >; - getProgramAccounts( + getProgramAccounts( program: Base58EncodedAddress, config: GetProgramAccountsApiCommonConfig & GetProgramAccountsApiSliceableCommonConfig & + TContextConfig & Readonly<{ encoding: 'base58'; - withContext: true; - }> - ): RpcResponse[]>; + }>, + ): GetProgramAccountsApiResponseWithContextConfig< + TContextConfig, + AccountInfoWithPubkey[] + >; - getProgramAccounts( + getProgramAccounts( program: Base58EncodedAddress, - config: GetProgramAccountsApiCommonConfig & - GetProgramAccountsApiSliceableCommonConfig & - Readonly<{ - encoding: 'base58'; - withContext?: boolean; - }> - ): AccountInfoWithPubkey[]; - - getProgramAccounts( - program: Base58EncodedAddress, - config: GetProgramAccountsApiCommonConfig & - GetProgramAccountsApiSliceableCommonConfig & - Readonly<{ - withContext: true; - }> - ): RpcResponse[]>; - - getProgramAccounts( - program: Base58EncodedAddress, - config?: GetProgramAccountsApiCommonConfig & - GetProgramAccountsApiSliceableCommonConfig & - Readonly<{ - withContext?: boolean; - }> - ): AccountInfoWithPubkey[]; + config?: GetProgramAccountsApiCommonConfig & GetProgramAccountsApiSliceableCommonConfig & TContextConfig, + ): GetProgramAccountsApiResponseWithContextConfig< + TContextConfig, + AccountInfoWithPubkey[] + >; } diff --git a/packages/rpc-transport/src/json-rpc-types.ts b/packages/rpc-transport/src/json-rpc-types.ts index 04efb1de8d82..6053742eeb5a 100644 --- a/packages/rpc-transport/src/json-rpc-types.ts +++ b/packages/rpc-transport/src/json-rpc-types.ts @@ -5,8 +5,8 @@ import { IRpcTransport } from './transports/transport-types'; */ export type IRpcApi = { [MethodName in keyof TRpcMethods]: TRpcMethods[MethodName] extends Callable - ? (...rawParams: unknown[]) => RpcRequest> - : never; + ? (...rawParams: Parameters) => RpcRequest> + : never; }; export type Rpc = RpcMethods; export type RpcConfig = Readonly<{ @@ -54,93 +54,38 @@ type PendingRpcRequestBuilder = UnionToIntersection< type Callable = (...args: any[]) => any; type Flatten = T extends (infer Item)[] ? Item : never; type Overloads = - // Have an RPC method with more than 10 overloads? Add another section and update this comment + // Have an RPC method with more than 5 overloads? Add another section and update this comment T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; (...args: infer A4): infer R4; (...args: infer A5): infer R5; - (...args: infer A6): infer R6; - (...args: infer A7): infer R7; - (...args: infer A8): infer R8; - (...args: infer A9): infer R9; - (...args: infer A10): infer R10; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5, (...args: A6) => R6, (...args: A7) => R7, (...args: A8) => R8, (...args: A9) => R9, (...args: A10) => R10] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - (...args: infer A6): infer R6; - (...args: infer A7): infer R7; - (...args: infer A8): infer R8; - (...args: infer A9): infer R9; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5, (...args: A6) => R6, (...args: A7) => R7, (...args: A8) => R8, (...args: A9) => R9] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - (...args: infer A6): infer R6; - (...args: infer A7): infer R7; - (...args: infer A8): infer R8; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5, (...args: A6) => R6, (...args: A7) => R7, (...args: A8) => R8] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - (...args: infer A6): infer R6; - (...args: infer A7): infer R7; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5, (...args: A6) => R6, (...args: A7) => R7] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - (...args: infer A6): infer R6; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5, (...args: A6) => R6] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - (...args: infer A5): infer R5; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - (...args: infer A4): infer R4; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - (...args: infer A3): infer R3; - } - ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3] - : T extends { - (...args: infer A1): infer R1; - (...args: infer A2): infer R2; - } - ? [(...args: A1) => R1, (...args: A2) => R2] - : T extends { - (...args: infer A1): infer R1; } - ? [(...args: A1) => R1] - : unknown; + ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4, (...args: A5) => R5] + : T extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + (...args: infer A4): infer R4; + } + ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3, (...args: A4) => R4] + : T extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + (...args: infer A3): infer R3; + } + ? [(...args: A1) => R1, (...args: A2) => R2, (...args: A3) => R3] + : T extends { + (...args: infer A1): infer R1; + (...args: infer A2): infer R2; + } + ? [(...args: A1) => R1, (...args: A2) => R2] + : T extends { + (...args: infer A1): infer R1; + } + ? [(...args: A1) => R1] + : unknown; type UnionToIntersection = (T extends unknown ? (x: T) => unknown : never) extends (x: infer R) => unknown ? R : never; diff --git a/packages/rpc-transport/src/json-rpc.ts b/packages/rpc-transport/src/json-rpc.ts index c01b530d1297..393806e0ab19 100644 --- a/packages/rpc-transport/src/json-rpc.ts +++ b/packages/rpc-transport/src/json-rpc.ts @@ -2,6 +2,8 @@ import { SolanaJsonRpcError } from './json-rpc-errors'; import { createJsonRpcMessage } from './json-rpc-message'; import { PendingRpcRequest, Rpc, RpcConfig, RpcRequest, SendOptions } from './json-rpc-types'; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Callable = (...args: any[]) => any; interface IHasIdentifier { readonly id: number; } @@ -38,7 +40,13 @@ function makeProxy(rpcConfig: RpcConfig): Rpc + : never + : never + ) { const methodName = p.toString(); const createRpcRequest = Reflect.get(target, methodName, receiver); const newRequest = createRpcRequest