1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-31 08:43:54 +00:00

feat: add types v1

This commit is contained in:
Andreas Coroiu
2025-10-22 14:19:34 +02:00
parent 453feb362f
commit 99cdae3f5b
3 changed files with 109 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
type AsChainable<T> = T extends object
? T extends Promise<T>
? T & { await: AwaitProxy<T> }
: Promise<T> & { await: AwaitProxy<T> }
: Promise<T>;
type AwaitProxy<T> = {
// Methods that return Promise<R> -> (...args) => Promise<R> (and if R is object, re-wrap to Chainable)
[K in keyof T]: T[K] extends (...args: infer A) => Promise<infer R>
? (...args: A) => AsChainable<R>
: // Sync methods -> (...args) => Promise<R> (and re-wrap objects)
T[K] extends (...args: infer A) => infer R
? (...args: A) => AsChainable<R>
: // Properties -> Promise<T[K]>
Promise<T[K]>;
};
export type ChainablePromise<T> = T extends object
? T extends Promise<T>
? T & { await: AwaitProxy<T> }
: Promise<T> & { await: AwaitProxy<T> }
: Promise<T>;
export function chain<T extends object>(p: Promise<T>): ChainablePromise<T> {
const promise: any = p;
if (!promise.await) {
const wrapIfObject = <U>(x: U): any =>
typeof x === "object" && x !== null ? chain(Promise.resolve(x as any)) : x;
promise.await = new Proxy(
{},
{
get(_t, prop: string | symbol) {
return (...args: any[]) =>
Promise.resolve(p).then(async (obj) => {
const member = (obj as any)[prop];
if (typeof member === "function") {
const result = await member.apply(obj, args);
return wrapIfObject(result);
}
// property access
return wrapIfObject(member);
});
},
},
);
}
return promise;
}
class A {
method(): ChainablePromise<B> {
return chain(B.createB());
}
}
class B {
static async createB(): Promise<B> {
return new B();
}
async asyncMethod(): Promise<B> {
return new B();
}
syncMethod(): number {
return 42;
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async function testChainable() {
const objectA = new A();
return objectA.method().await.asyncMethod().await.syncMethod();
}

View File

@@ -0,0 +1,11 @@
import { BitwardenClient, FolderView } from "@bitwarden/sdk-internal";
import { Remote } from "./remote";
export type RemoteSdk = Remote<BitwardenClient>;
const remoteClient: RemoteSdk = {} as RemoteSdk;
export async function test(): Promise<FolderView[]> {
return await remoteClient.vault().await.folders().await.list();
}

View File

@@ -0,0 +1,21 @@
import { ChainablePromise } from "./chainable-promise";
export type Remote<T> = {
[K in keyof T]: RemoteProperty<T[K]>;
};
export type RemoteProperty<T> = T extends (...args: any[]) => any
? RemoteFunction<T>
: RemoteValue<T>;
export type RemoteReference<T> = Remote<T>;
export type RemoteValue<T> = T extends { free(): void }
? ChainablePromise<RemoteReference<T>>
: T extends Promise<infer R>
? Promise<R>
: Promise<T>;
export type RemoteFunction<T extends (...args: any[]) => any> = (
...args: Parameters<T>
) => RemoteValue<ReturnType<T>>;