Compile TypeScript Types for runtime use

I have a client server application that communicates using REST calls.

To prevent that I accedently use the wrong types I defined all RestCalls in a common file (excerpt):

type def<TConnection extends Connections> =
    // Authentication
    TConnection extends '/auth/password/check/:login->get' ? Set<void, { found: boolean }, void, false>
    : TConnection extends '/auth/password/register->post' ? Set<RegsiterAccount<Login>, void, void, false>
    : TConnection extends '/auth/password/login->post' ? Set<Login, void, void, false>
    : TConnection extends '/auth/webauth/challenge->get' ? Set<void, {
        challenge: string,
        id: string
    }, void, false>
    : TConnection extends '/auth/webauth/register->post' ? Set<RegsiterAccount<WebAuthN> & { comment: string }, void, void, false>
    : TConnection extends '/auth/webauth/login->post' ? Set<Assertion, void, void, false>
    : TConnection extends '/auth/logout->post' ? Set<void, void, void>
    : TConnection extends '/auth/invite->get' ? Set<void, {
        link: string,
        validUntill: string
    }, void>
    : TConnection extends '/auth/invite/validate->post' ? Set<{ invite: string }, {
        granted_by: string,
        validUntill: string
    }, void, false>
    : TConnection extends '/auth/isAuthenticated->get' ? Set<void, {
        isAuthenticated: boolean,
        userName: string | undefined
    }, void, false>

    // default
    : never

The url and method are encoded in the string, it also uses express url parameters (/:). Set defines the data in the body and if the server should check authentication

  1. request
  2. response
  3. response on error
  4. if authentication is needed
type Set<Input extends (Object | void), result extends (Object | void), Error extends string | object | void, NeedsAuthentication extends boolean = true> = {
    input: Input, result: result,
    error: DefaultError<Error>,
    authenticated: NeedsAuthentication
};

I can then use following types to get the correct values

export type InputBody<TPath extends Connections> = def<TPath>['input'];
export type InputPath<TPath extends Connections> = express.RouteParameters<TPath>;
export type Input<TPath extends Connections> = InputBody<TPath> & InputPath<TPath>;


export type Result<TPath extends Connections> = def<TPath>['result']
export type NeedsAuthentication<TPath extends Connections> = def<TPath>['authenticated']

export type Error<TPath extends Connections> = def<TPath>['error']

export type Method<T extends string> = T extends `${infer path}->${infer method}`
    ? method extends METHODS ? method
    : never
    : never;
export type Path<T extends string> = T extends `${infer path}->${infer method}`

    ? method extends METHODS ? path
    : never
    : never;

I would now like to know at runtime if for a specific call authentication is required.

I could encode it in the url like I did for the method. Or use NeedsAuthentication as a paramter where the url is also provided. That way when I put in the URL I get autocomplete for that will only have true if authentication is needed otherwise false.

I don't like both workarounds.

What I would like to do is

const needsAuthentication :boolean = NeedsAuthentication<'/auth/webauth/login->post'>;

Is there any way to tell the compiler to compile the types in the output JS so I cann do the same thing the compiler does when interfereing what a parameter needs to be?

My only other solution currently is writing a script that is executed prebuild which parses my definition and put out a map that contains for every URL if it needs authentication using some regex parsing...


EDIT

I would like to implement following function

function needsAuthentication<T extends Connections>(test:T):NeedsAuthentication<T> {
    // todo find out if authentication is actual required for this url 
}

which is not part of the transmited data but encoded in the type mapping.

The compiler will nicely map it to true or false sample completion 1 sample completion 2 for const strings on compieltime (the actual value would still not be emiited...) sample completion 3

I could try to use the typescript compiler and call whatever function evaluates the return values...


Solution 1:

No. Typescript types will be emitted during the compilation phase.

Your options are:

  1. Use JSON-Schema (Ajv) to validat the input of incoming http json requests: https://github.com/ajv-validator/ajv
  2. Use Swagger (Almost the same as (1)).
  3. Use a validator which works with your framework.
  4. I found this project which tries to create runtime asserts from typescript types: https://github.com/skunkteam/types. But I never used it my self.
  5. https://github.com/nanoporetech/ts-runtime-typecheck - Same as (4) but never used it as well.