Propozycja typowania Context

Hej! Ostatnie trzy posty w tej kategorii były odemnie i nie inaczej jest tym razem. Moim potajemnym planem jest zdominowanie tej kategorii. Dzisiaj przychodze z czymś co wymyśliłem w trakcie codereview taska do [projektu komercyjnego]. Nie będę wrzucał tutaj za dużo kodu tak, żeby mnie Kuba za NDA nie dojechał więc się ograniczę do tych fragmentów co nie ujawniają niczego.

Mianowicie chciałem użyć context jako argument funkcji i odwołać się w niej do pola które miała klasa rozszerzająca App

(ctx.app as TheApp).mssql //...

Przez to potrzebowałem własnie zrobić taką assercję, które w tym akurat projekcie są przez linter zabronione. Wymyśliłem więc, że może ten typ może przyjmować template typu jakiego aplikacja jest. Zrobiłem nawet działający proof of concetp. Najpierw dodałem taki templatedo kontextu w sealiousie

import App from "./app/app";
import { CollectionItem } from "./main";
export default class Context<T extends App = App> {
    app: T;
    timestamp: number;
    ip: string | null;
    user_id: string | null;
    user_data: null | Promise<CollectionItem>;
    loading_user_data: boolean;
    session_id: string | null;
    is_super: boolean;
    original_context: Context | null;
    constructor(app: App, timestamp?: number, user_id?: string | null, session_id?: string | null);
    getUserData(app: App): Promise<CollectionItem | null>;
    toDBEntry(): {
        timestamp: number;
        ip: string | null;
        user_id: string | null;
        session_id: string | null;
    };
}
export declare class SuperContext extends Context {
    is_super: boolean;
}

Następnie w samej aplikacji już dodałem to

import Mailer from "../email/mailer";
import Emittery from "emittery";
import Datastore from "../datastore/datastore";
import Metadata from "./metadata";
import { PartialConfig } from "./config";
import { ManifestData } from "./manifest";
import Logger from "./logger";
import ConfigManager from "./config-manager";
import HttpServer from "../http/http";
import Context, { SuperContext } from "../context";
import Collection from "../chip-types/collection";
import Users from "./collections/users";
import Sessions from "./collections/sessions";
export declare type AppEvents = "starting" | "started" | "stopping" | "stopped";
/** The heart of your, well app. It all starts with  `new App(...)` */
declare abstract class App {
    /** The current status of the app */
    status: "stopped" | "running" | "starting" | "stopping";
    // ...
    /** A shorthand-way to create a new context: `new app.Context()`. */
    Context: new () => Context<this>;

Potem jako wisienka na torcie musieliśmy powiedzieć jakiej aplikacji kontext nasz BaseContext z koa przechowuje

declare module "koa" {
	interface BaseContext {
		$context: Sealious.Context<TheApp>;
		$app: TheApp;
		$body: Record<string, unknown>;
	}
}

Po tych skomplikowanych operacjach byłem w stanie zrobić taką deklarację fukncji

	async totalnieAnonimowaFunkcja(
		ctx: Context<TheApp>
	): Promise<(Invoice & Contractor & { Id: number[] })[]> {
		return await ctx.app
			.mssql //...

Nie jest to gotowe rozwiązanie wiadomo, a jedynie koncept mogłem w tym pominąć ważne rzeczy, ale sam pomysł wydał mi się ciekawy i chciałem go tu na formum zaproponować. Po tych zmianach z sukcesem odpliłem aplikację oraz przeszły wszsystkie testy, a jednocześnie pozbyłem się tych assercji.

Dajcie znać co o tym myślicie czy już oszalałem do reszty czy nie xd

1 Like

Super! Głowiłem się nad tym, jak to ugryźć elegancko. Można byłoby połączyć z tym taskiem na unifikację contextu Koa i Sealiousowego:

https://hub.sealcode.org/T2595

Wygląda jak dobre miejsce żeby się tym zająć :smiley:

1 Like