/** * TypeScript utility types for verifying that two interfaces have matching method signatures. * * This is particularly useful for ensuring that typed and untyped versions of interfaces * stay in sync when one has generics and the other doesn't. * * @example * ```typescript * import type { InterfaceAssertion } from "./InterfaceVerification"; * * interface BaseModule extends HybridObject<{ ios: "swift" }> { * getData(id: string): Promise; * } * * interface BaseModuleTyped { * getData(id: T): Promise; * } * * // This will cause a TypeScript error if the interfaces don't match * const _verification: InterfaceAssertion = true; * ``` */ /** * Extracts method names from an interface, excluding specified keys */ export type ExtractMethodNames< T, ExcludeKeys extends keyof T = never, > = Exclude /** * Gets the parameter count of a function type */ export type GetParameterCount = T extends (...args: infer P) => unknown ? P['length'] : never /** * Checks if two interfaces have the same method names */ export type CheckMethodNames< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = { BaseMethodNames: ExtractMethodNames TypedMethodNames: ExtractMethodNames MissingInBase: Exclude< ExtractMethodNames, ExtractMethodNames > MissingInTyped: Exclude< ExtractMethodNames, ExtractMethodNames > InterfaceMismatch: | Exclude< ExtractMethodNames, ExtractMethodNames > | Exclude< ExtractMethodNames, ExtractMethodNames > } /** * Checks parameter counts for shared methods between two interfaces */ export type CheckParameterCounts< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = { SharedMethodNames: ExtractMethodNames & ExtractMethodNames ParameterCountMismatches: { [K in ExtractMethodNames & ExtractMethodNames]: GetParameterCount< BaseInterface[K] > extends GetParameterCount ? GetParameterCount extends GetParameterCount< BaseInterface[K] > ? never : K : K }[ExtractMethodNames & ExtractMethodNames] } /** * Checks that typed methods remain assignable to their base counterparts. */ export type CheckMethodCompatibility< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = { SharedMethodNames: ExtractMethodNames & ExtractMethodNames IncompatibleMethods: { [K in ExtractMethodNames & ExtractMethodNames]: TypedInterface[K] extends BaseInterface[K] ? never : K }[ExtractMethodNames & ExtractMethodNames] } /** * Complete interface verification that checks both method names and parameter counts */ export type VerifyInterfaceSync< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = { MethodCheck: CheckMethodNames ParameterCheck: CheckParameterCounts< BaseInterface, TypedInterface, ExcludeFromBase > CompatibilityCheck: CheckMethodCompatibility< BaseInterface, TypedInterface, ExcludeFromBase > // Final verification result Result: CheckMethodNames< BaseInterface, TypedInterface, ExcludeFromBase >['InterfaceMismatch'] extends never ? CheckParameterCounts< BaseInterface, TypedInterface, ExcludeFromBase >['ParameterCountMismatches'] extends never ? CheckMethodCompatibility< BaseInterface, TypedInterface, ExcludeFromBase >['IncompatibleMethods'] extends never ? true : { ERROR: 'Method signature compatibility mismatch detected' IncompatibleMethods: CheckMethodCompatibility< BaseInterface, TypedInterface, ExcludeFromBase >['IncompatibleMethods'] } : { ERROR: 'Parameter count mismatch detected' MethodsWithParameterCountMismatch: CheckParameterCounts< BaseInterface, TypedInterface, ExcludeFromBase >['ParameterCountMismatches'] } : { ERROR: 'Interface mismatch detected' MissingInBase: CheckMethodNames< BaseInterface, TypedInterface, ExcludeFromBase >['MissingInBase'] MissingInTyped: CheckMethodNames< BaseInterface, TypedInterface, ExcludeFromBase >['MissingInTyped'] } } /** * Simplified interface verification for common use cases */ export type AssertInterfacesMatch< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = VerifyInterfaceSync< BaseInterface, TypedInterface, ExcludeFromBase >['Result'] /** * Creates a compile-time assertion that two interfaces match * Usage: const _check: InterfaceAssertion = true; */ export type InterfaceAssertion< BaseInterface, TypedInterface, ExcludeFromBase extends keyof BaseInterface = never, > = AssertInterfacesMatch