typizator
Version:
Runtime types and metadata schemas for Typescript
182 lines (181 loc) • 5.06 kB
TypeScript
import { Schema } from "./schemas";
import { InferTargetFromSchema } from "./type-conversions";
/**
* Defines the run-time function call schema. To be used by facades, serializers, etc...
*/
export type FunctionCallDefinition = {
/**
* List of arguments types schemas
*/
args: [Schema?, ...Schema[]];
/**
* Return value type schema
*/
retVal?: Schema;
/**
* If true, it tells the integration to exclude this function from the API interface
*/
hidden?: boolean;
};
/**
* Information
*/
export type NamedMetadata = {
/**
* Name of the function or of the child API
*/
name: string;
/**
* Full path from the root of the API, separated by slashes
*/
path: string;
};
/**
* Metadata for an API member function
*/
export type FunctionMetadata = {
/**
* Allows to make the difference between functions and sub-apis
*/
dataType: "function";
/**
* List of arguments types schemas
*/
args: Schema[];
/**
* Return value type schema
*/
retVal: Schema;
/**
* If true, it tells the integration to exclude this function from the API interface
*/
hidden?: boolean;
} & NamedMetadata;
/**
* List of function definitions and sub-apis
*
* The field name can be any valid typescript identifier except **name**, **path** and **metadata**
*/
export type ApiDefinition = {
[K: string]: FunctionCallDefinition | ApiDefinition;
} & {
hidden?: boolean;
} & {
metadata?: never;
};
/**
* Reproduces the API tree but with additional information like names and paths
*/
export type MetadataMembersImplementation<T extends ApiDefinition> = {
[K in keyof T]: T[K] extends ApiDefinition ? MetadataMembersImplementation<T[K]> & {
metadata: ApiMetadata<T[K]>;
} : T[K] & {
metadata: FunctionMetadata;
};
};
/**
* Metadata for the API endpoint
*/
export type ApiMetadata<T extends ApiDefinition> = {
/**
* Allows to make the difference between functions and sub-apis
*/
dataType: "api";
/**
* Objects metadata tree reproducing the API structure
*/
implementation: MetadataMembersImplementation<T>;
/**
* If true, it tells the integration to exclude this API from the API interface
*/
hidden?: boolean;
} & NamedMetadata;
/**
* Run-time metadata giving the run-time access to the API structure and data types
*/
export interface ApiSchema<T extends ApiDefinition, _ extends {
hidden?: boolean;
}> {
get metadata(): ApiMetadata<T>;
}
/**
* Creates a new API schema
* @param definition Object containing function definitions matching `FunctionCallDefinition` and sub-apis allowing to build a tree API structure
* @param props Optional properties to apply to the API. `hidden` as true will hide the API from the API interface
* @returns API schema available at fun time
*/
export declare const apiS: <T extends ApiDefinition, P extends {
hidden?: boolean | undefined;
}>(definition: T, props?: P) => ApiSchema<T, P>;
/**
* Extracts argument types from a list of schemas.
*
* @example
* This:
* ```ts
* [intS, stringS]
* ```
* ...becomes this:
* ```ts
* [number,string]
* ```
*/
export type InferArguments<T extends [...any]> = T extends [...infer P] ? {
[K in keyof P]: P[K] extends Schema ? InferTargetFromSchema<P[K]> : never;
} : never;
/**
* Extracts the type from the API schema
*
* @example
* This:
* ```ts
* apiS({
* helloWorld: { args: [stringS, intS], retVal: bigintS }
* cruel: {
* world: { args:[] }
* }
* })
* ```
* ...becomes this:
* ```ts
* {
* helloWorld: (arg0:string, arg1:number) => Promise<bigint>
* cruel: {
* world: () => Promise<void>
* }
* }
* ```
*/
export type ApiImplementation<T> = T extends ApiSchema<infer S, any> ? ApiImplementation<S> : {
[K in keyof T]: T[K] extends ApiDefinition ? ApiImplementation<T[K]> : T[K] extends FunctionCallDefinition ? (...args: InferArguments<T[K]["args"]>) => Promise<InferTargetFromSchema<T[K]["retVal"]>> : never;
};
/**
* Extracts the type from the API schema taking into account the visibility (`hidden` property) of the API and its functions
*
* @example
* This:
* ```ts
* apiS({
* helloWorld: { args: [stringS, intS], retVal: bigintS }
* cruel: {
* world: { args:[] }
* }
* })
* ```
* ...becomes this:
* ```ts
* {
* helloWorld: (arg0:string, arg1:number) => Promise<bigint>
* cruel: {
* world: () => Promise<void>
* }
* }
* ```
*/
export type ApiImplementationWithVisibility<T> = T extends ApiSchema<infer S, infer P> ? P extends {
hidden: true;
} ? never : ApiImplementationWithVisibility<S> : {
[K in keyof T as T[K] extends {
hidden: true;
} ? never : K]: T[K] extends ApiDefinition ? ApiImplementationWithVisibility<T[K]> : T[K] extends FunctionCallDefinition ? (...args: InferArguments<T[K]["args"]>) => Promise<InferTargetFromSchema<T[K]["retVal"]>> : never;
};