From f894d978df4fbc230db6a7f156e7b3c3844ab779 Mon Sep 17 00:00:00 2001 From: syuilo Date: Sun, 20 Jun 2021 00:43:29 +0900 Subject: [PATCH] Improve type definitions --- src/api.types.ts | 60 ++++++++++++++++++++++++++++++++++-------------- src/entities.ts | 18 +++++++++++++-- test-d/api.ts | 15 +++++++++++- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/src/api.types.ts b/src/api.types.ts index 5d9b4600b..b2314db9b 100644 --- a/src/api.types.ts +++ b/src/api.types.ts @@ -1,7 +1,8 @@ import { - Ad, Announcement, Antenna, App, AuthSession, Clip, DetailedInstanceMetadata, DriveFile, DriveFolder, FollowRequest, GalleryPost, InstanceMetadata, + Ad, Announcement, Antenna, App, AuthSession, Channel, Clip, DateString, DetailedInstanceMetadata, DriveFile, DriveFolder, FollowRequest, GalleryPost, InstanceMetadata, LiteInstanceMetadata, - Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserGroup, UserList, UserSorting + MeDetailed, + Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting } from './entities'; type TODO = Record | null; @@ -340,24 +341,24 @@ export type Endpoints = { 'i/notifications': { req: TODO; res: TODO; }; 'i/page-likes': { req: TODO; res: TODO; }; 'i/pages': { req: TODO; res: TODO; }; - 'i/pin': { req: TODO; res: TODO; }; + 'i/pin': { req: { noteId: Note['id']; }; res: MeDetailed; }; 'i/read-all-messaging-messages': { req: TODO; res: TODO; }; 'i/read-all-unread-notes': { req: TODO; res: TODO; }; 'i/read-announcement': { req: TODO; res: TODO; }; - 'i/regenerate-token': { req: TODO; res: TODO; }; - 'i/registry/get-all': { req: TODO; res: TODO; }; - 'i/registry/get-detail': { req: TODO; res: TODO; }; - 'i/registry/get': { req: TODO; res: TODO; }; - 'i/registry/keys-with-type': { req: TODO; res: TODO; }; - 'i/registry/keys': { req: TODO; res: TODO; }; - 'i/registry/remove': { req: TODO; res: TODO; }; - 'i/registry/scopes': { req: TODO; res: TODO; }; - 'i/registry/set': { req: TODO; res: TODO; }; + 'i/regenerate-token': { req: { password: string; }; res: null; }; + 'i/registry/get-all': { req: { scope?: string[]; }; res: Record; }; + 'i/registry/get-detail': { req: { key: string; scope?: string[]; }; res: { updatedAt: DateString; value: any; }; }; + 'i/registry/get': { req: { key: string; scope?: string[]; }; res: any; }; + 'i/registry/keys-with-type': { req: { scope?: string[]; }; res: Record; }; + 'i/registry/keys': { req: { scope?: string[]; }; res: string[]; }; + 'i/registry/remove': { req: { key: string; scope?: string[]; }; res: null; }; + 'i/registry/scopes': { req: {}; res: string[][]; }; + 'i/registry/set': { req: { key: string; value: any; scope?: string[]; }; res: null; }; 'i/revoke-token': { req: TODO; res: TODO; }; 'i/signin-history': { req: TODO; res: TODO; }; 'i/unpin': { req: TODO; res: TODO; }; 'i/update-email': { req: TODO; res: TODO; }; - 'i/update': { req: TODO; res: User; }; + 'i/update': { req: TODO; res: MeDetailed; }; 'i/user-group-invites': { req: TODO; res: TODO; }; 'i/2fa/done': { req: TODO; res: TODO; }; 'i/2fa/key-done': { req: TODO; res: TODO; }; @@ -401,7 +402,24 @@ export type Endpoints = { 'notes/children': { req: { noteId: Note['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; }; res: Note[]; }; 'notes/clips': { req: TODO; res: TODO; }; 'notes/conversation': { req: TODO; res: TODO; }; - 'notes/create': { req: TODO; res: { createdNote: Note }; }; + 'notes/create': { req: { + visibility?: 'public' | 'home' | 'followers' | 'specified', + visibleUserIds?: User['id'][]; + text?: null | string; + cw?: null | string; + viaMobile?: boolean; + localOnly?: boolean; + fileIds?: DriveFile['id'][]; + replyId?: null | Note['id']; + renoteId?: null | Note['id']; + channelId?: null | Channel['id']; + poll?: null | { + choices: string[]; + multiple?: boolean; + expiresAt?: null | number; + expiredAfter?: null | number; + }; + }; res: { createdNote: Note }; }; 'notes/delete': { req: { noteId: Note['id']; }; res: null; }; 'notes/favorites/create': { req: TODO; res: TODO; }; 'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; }; @@ -413,7 +431,7 @@ export type Endpoints = { 'notes/polls/recommendation': { req: TODO; res: TODO; }; 'notes/polls/vote': { req: TODO; res: TODO; }; 'notes/reactions': { req: TODO; res: TODO; }; - 'notes/reactions/create': { req: TODO; res: TODO; }; + 'notes/reactions/create': { req: { noteId: Note['id']; reaction: string; }; res: null; }; 'notes/reactions/delete': { req: { noteId: Note['id']; }; res: null; }; 'notes/renotes': { req: TODO; res: TODO; }; 'notes/replies': { req: TODO; res: TODO; }; @@ -500,13 +518,21 @@ export type Endpoints = { 'users/lists/push': { req: { listId: UserList['id']; userId: User['id']; }; res: null; }; 'users/lists/show': { req: { listId: UserList['id']; }; res: UserList; }; 'users/lists/update': { req: { listId: UserList['id']; name: string; }; res: UserList; }; - 'users/notes': { req: TODO; res: TODO; }; + 'users/notes': { req: { userId: User['id']; limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; }; 'users/pages': { req: TODO; res: TODO; }; 'users/recommendation': { req: TODO; res: TODO; }; 'users/relation': { req: TODO; res: TODO; }; 'users/report-abuse': { req: TODO; res: TODO; }; 'users/search-by-username-and-host': { req: TODO; res: TODO; }; 'users/search': { req: TODO; res: TODO; }; - 'users/show': { req: ShowUserReq; res: User; } | { req: { userIds: User['id'][]; }; res: User[]; }; + 'users/show': { req: ShowUserReq | { userIds: User['id'][]; }; res: { + $switch: { + $cases: [[ + { userIds: User['id'][]; }, + UserDetailed[], + ]]; + $default: UserDetailed; + }; + }; }; 'users/stats': { req: TODO; res: TODO; }; }; diff --git a/src/entities.ts b/src/entities.ts index fd3f4f260..775261ba0 100644 --- a/src/entities.ts +++ b/src/entities.ts @@ -3,7 +3,10 @@ export type DateString = string; type TODO = Record; -export type User = { +// NOTE: 極力この型を使うのは避け、UserLite か UserDetailed か明示するように +export type User = UserLite | UserDetailed; + +export type UserLite = { id: ID; username: string; host: string | null; @@ -17,6 +20,12 @@ export type User = { }[]; }; +export type UserDetailed = UserLite & { + isLocked: boolean; + pinnedNotes: Note[]; + // TODO +}; + export type UserGroup = TODO; export type UserList = { @@ -26,7 +35,7 @@ export type UserList = { userIds: User['id'][]; }; -export type MeDetailed = User & { +export type MeDetailed = UserDetailed & { avatarId: DriveFile['id']; bannerId: DriveFile['id']; autoAcceptFollowed: boolean; @@ -307,5 +316,10 @@ export type FollowRequest = { followee: User; }; +export type Channel = { + id: ID; + // TODO +}; + export type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+updatedAt' | '-updatedAt'; export type OriginType = 'combined' | 'local' | 'remote'; diff --git a/test-d/api.ts b/test-d/api.ts index a9e9d403b..f3f924fee 100644 --- a/test-d/api.ts +++ b/test-d/api.ts @@ -11,7 +11,7 @@ describe('API', () => { expectType(res); }); - test('conditional respose type', async () => { + test('conditional respose type (meta)', async () => { const cli = new Misskey.api.APIClient({ origin: 'https://misskey.test', credential: 'TOKEN' @@ -26,4 +26,17 @@ describe('API', () => { const res3 = await cli.request('meta', { }); expectType(res3); }); + + test('conditional respose type (users/show)', async () => { + const cli = new Misskey.api.APIClient({ + origin: 'https://misskey.test', + credential: 'TOKEN' + }); + + const res = await cli.request('users/show', { userId: 'xxxxxxxx' }); + expectType(res); + + const res2 = await cli.request('users/show', { userIds: ['xxxxxxxx'] }); + expectType(res2); + }); });