型レベルでのswitch-case機構をmisskey-jsからfrontendに持ち込めるようにした

This commit is contained in:
osamu 2023-11-25 15:39:52 +09:00
parent 5f88d62f87
commit de37897f92
3 changed files with 52 additions and 41 deletions

View File

@ -10,7 +10,12 @@ import { $i } from '@/account.js';
export const pendingApiRequestsCount = ref(0);
// Implements Misskey.api.ApiClient.request
export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(endpoint: E, data: P = {} as any, token?: string | null | undefined, signal?: AbortSignal): Promise<Misskey.Endpoints[E]['res']> {
export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
endpoint: E,
data: P = {} as any,
token?: string | null | undefined,
signal?: AbortSignal,
): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
if (endpoint.includes('://')) throw new Error('invalid endpoint');
pendingApiRequestsCount.value++;

View File

@ -1,6 +1,10 @@
import { SwitchCase } from './api.types';
import { SwitchCaseResponseType } from './api.types';
import type { Endpoints } from './api.types';
export {
SwitchCaseResponseType,
} from './api.types';
const MK_API_ERROR = Symbol();
export type APIError = {
@ -16,28 +20,15 @@ export function isAPIError(reason: any): reason is APIError {
}
export type FetchLike = (input: string, init?: {
method?: string;
body?: string;
credentials?: RequestCredentials;
cache?: RequestCache;
headers: {[key in string]: string}
}) => Promise<{
status: number;
json(): Promise<any>;
}>;
type IsNeverType<T> = [T] extends [never] ? true : false;
type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false
: false
type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
: never
method?: string;
body?: string;
credentials?: RequestCredentials;
cache?: RequestCache;
headers: { [key in string]: string }
}) => Promise<{
status: number;
json(): Promise<any>;
}>;
export class APIClient {
public origin: string;
@ -60,20 +51,7 @@ export class APIClient {
endpoint: E,
params: P = {} as P,
credential?: string | null,
): Promise<Endpoints[E]['res'] extends SwitchCase
?
IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
Endpoints[E]['res']['$switch']['$default'] :
Endpoints[E]['res']> {
): Promise<SwitchCaseResponseType<E, P>> {
return new Promise((resolve, reject) => {
this.fetch(`${this.origin}/api/${endpoint}`, {
method: 'POST',

View File

@ -2,18 +2,44 @@ import { Endpoints as Gen } from './autogen/endpoint';
import { UserDetailed } from './autogen/models';
import { UsersShowRequest } from './autogen/entities';
export type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
T,
keyof U
> & U;
export type SwitchCase = {
type SwitchCase = {
$switch: {
$cases: [any, any][],
$default: any;
};
};
type IsNeverType<T> = [T] extends [never] ? true : false;
type StrictExtract<Union, Cond> = Cond extends Union ? Union : never;
type IsCaseMatched<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? IsNeverType<StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>> extends false ? true : false
: false
type GetCaseResult<E extends keyof Endpoints, P extends Endpoints[E]['req'], C extends number> =
Endpoints[E]['res'] extends SwitchCase
? StrictExtract<Endpoints[E]['res']['$switch']['$cases'][C], [P, any]>[1]
: never
export type SwitchCaseResponseType<E extends keyof Endpoints, P extends Endpoints[E]['req']> = Endpoints[E]['res'] extends SwitchCase
? IsCaseMatched<E, P, 0> extends true ? GetCaseResult<E, P, 0> :
IsCaseMatched<E, P, 1> extends true ? GetCaseResult<E, P, 1> :
IsCaseMatched<E, P, 2> extends true ? GetCaseResult<E, P, 2> :
IsCaseMatched<E, P, 3> extends true ? GetCaseResult<E, P, 3> :
IsCaseMatched<E, P, 4> extends true ? GetCaseResult<E, P, 4> :
IsCaseMatched<E, P, 5> extends true ? GetCaseResult<E, P, 5> :
IsCaseMatched<E, P, 6> extends true ? GetCaseResult<E, P, 6> :
IsCaseMatched<E, P, 7> extends true ? GetCaseResult<E, P, 7> :
IsCaseMatched<E, P, 8> extends true ? GetCaseResult<E, P, 8> :
IsCaseMatched<E, P, 9> extends true ? GetCaseResult<E, P, 9> :
Endpoints[E]['res']['$switch']['$default'] : Endpoints[E]['res'];
export type Endpoints = Overwrite<
Gen,
{
@ -22,7 +48,9 @@ export type Endpoints = Overwrite<
res: {
$switch: {
$cases: [[
{ userIds: string[]; }, UserDetailed[],
{
userIds?: string[];
}, UserDetailed[],
]];
$default: UserDetailed;
};