types(util): add case utility types (#683)

* types(util): add case utility types

* Rename *ID to *Id
This commit is contained in:
ayntee
2021-03-23 19:04:46 +04:00
committed by GitHub
parent 6ff4654686
commit 10cb9d7eb9
10 changed files with 161 additions and 48 deletions
+2 -2
View File
@@ -66,9 +66,9 @@ Examples of bad PR title:
Example: Example:
```ts ```ts
// Discordeno has utility type Camelize<T>, where T is an interface with keys in snake case. // Discordeno has utility type CamelCaseProps<T>, where T is an interface with keys in snake case.
// It can be used to "generate" corresponding "Discordeno type" from "Discord type". // It can be used to "generate" corresponding "Discordeno type" from "Discord type".
// Example: export type BanOptions = Camelize<DiscordBanOptions> // Example: export type BanOptions = CamelCaseProps<DiscordBanOptions>
export interface EditMemberOptions { export interface EditMemberOptions {
/** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */
@@ -5,14 +5,14 @@ export function handleApplicationCommandCreate(
data: DiscordPayload, data: DiscordPayload,
) { ) {
const { const {
guild_id: guildID, guild_id: guildId,
application_id: applicationID, application_id: applicationId,
...rest ...rest
} = data.d as ApplicationCommandEvent; } = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandCreate?.({ eventHandlers.applicationCommandCreate?.({
...rest, ...rest,
guildID, guildId,
applicationID, applicationId,
}); });
} }
@@ -3,14 +3,14 @@ import { ApplicationCommandEvent, DiscordPayload } from "../../types/mod.ts";
export function handleApplicationCommandDelete(data: DiscordPayload) { export function handleApplicationCommandDelete(data: DiscordPayload) {
const { const {
application_id: applicationID, application_id: applicationId,
guild_id: guildID, guild_id: guildId,
...rest ...rest
} = data.d as ApplicationCommandEvent; } = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandDelete?.({ eventHandlers.applicationCommandDelete?.({
...rest, ...rest,
guildID, guildId,
applicationID, applicationId,
}); });
} }
@@ -3,14 +3,14 @@ import { ApplicationCommandEvent, DiscordPayload } from "../../types/mod.ts";
export function handleApplicationCommandUpdate(data: DiscordPayload) { export function handleApplicationCommandUpdate(data: DiscordPayload) {
const { const {
application_id: applicationID, application_id: applicationId,
guild_id: guildID, guild_id: guildId,
...rest ...rest
} = data.d as ApplicationCommandEvent; } = data.d as ApplicationCommandEvent;
eventHandlers.applicationCommandUpdate?.({ eventHandlers.applicationCommandUpdate?.({
...rest, ...rest,
guildID, guildId,
applicationID, applicationId,
}); });
} }
@@ -8,24 +8,24 @@ export function handleIntegrationCreate(
data: DiscordPayload, data: DiscordPayload,
) { ) {
const { const {
guild_id: guildID, guild_id: guildId,
enable_emoticons: enableEmoticons, enable_emoticons: enableEmoticons,
expire_behavior: expireBehavior, expire_behavior: expireBehavior,
expire_grace_period: expireGracePeriod, expire_grace_period: expireGracePeriod,
subscriber_count: subscriberCount, subscriber_count: subscriberCount,
role_id: roleID, role_id: roleId,
synced_at: syncedAt, synced_at: syncedAt,
...rest ...rest
} = data.d as IntegrationCreateUpdateEvent; } = data.d as IntegrationCreateUpdateEvent;
eventHandlers.integrationCreate?.({ eventHandlers.integrationCreate?.({
...rest, ...rest,
guildID, guildId,
enableEmoticons, enableEmoticons,
expireBehavior, expireBehavior,
expireGracePeriod, expireGracePeriod,
syncedAt, syncedAt,
subscriberCount, subscriberCount,
roleID, roleId,
}); });
} }
@@ -3,14 +3,14 @@ import { DiscordPayload, IntegrationDeleteEvent } from "../../types/mod.ts";
export function handleIntegrationDelete(data: DiscordPayload) { export function handleIntegrationDelete(data: DiscordPayload) {
const { const {
guild_id: guildID, guild_id: guildId,
application_id: applicationID, application_id: applicationId,
...rest ...rest
} = data.d as IntegrationDeleteEvent; } = data.d as IntegrationDeleteEvent;
eventHandlers.integrationDelete?.({ eventHandlers.integrationDelete?.({
...rest, ...rest,
applicationID, applicationId,
guildID, guildId,
}); });
} }
@@ -9,20 +9,20 @@ export function handleIntegrationUpdate(data: DiscordPayload) {
enable_emoticons: enableEmoticons, enable_emoticons: enableEmoticons,
expire_behavior: expireBehavior, expire_behavior: expireBehavior,
expire_grace_period: expireGracePeriod, expire_grace_period: expireGracePeriod,
role_id: roleID, role_id: roleId,
subscriber_count: subscriberCount, subscriber_count: subscriberCount,
synced_at: syncedAt, synced_at: syncedAt,
guild_id: guildID, guild_id: guildId,
...rest ...rest
} = data.d as IntegrationCreateUpdateEvent; } = data.d as IntegrationCreateUpdateEvent;
eventHandlers.integrationUpdate?.({ eventHandlers.integrationUpdate?.({
...rest, ...rest,
guildID, guildId,
subscriberCount, subscriberCount,
enableEmoticons, enableEmoticons,
expireGracePeriod, expireGracePeriod,
roleID, roleId,
expireBehavior, expireBehavior,
syncedAt, syncedAt,
}); });
+4 -4
View File
@@ -5,10 +5,10 @@ export function handleInviteCreate(payload: DiscordPayload) {
if (payload.t !== "INVITE_CREATE") return; if (payload.t !== "INVITE_CREATE") return;
//TODO: replace with tocamelcase //TODO: replace with tocamelcase
const { const {
channel_id: channelID, channel_id: channelId,
created_at: createdAt, created_at: createdAt,
max_age: maxAge, max_age: maxAge,
guild_id: guildID, guild_id: guildId,
target_user: targetUser, target_user: targetUser,
target_user_type: targetUserType, target_user_type: targetUserType,
max_uses: maxUses, max_uses: maxUses,
@@ -17,8 +17,8 @@ export function handleInviteCreate(payload: DiscordPayload) {
eventHandlers.inviteCreate?.({ eventHandlers.inviteCreate?.({
...rest, ...rest,
channelID, channelId,
guildID, guildId,
maxAge, maxAge,
targetUser, targetUser,
targetUserType, targetUserType,
+15 -9
View File
@@ -25,7 +25,7 @@ import {
PartialMessage, PartialMessage,
ReactionPayload, ReactionPayload,
} from "./message.ts"; } from "./message.ts";
import { Camelize } from "./util.ts"; import { CamelCaseProps } from "./util.ts";
export interface BotConfig { export interface BotConfig {
token: string; token: string;
@@ -93,15 +93,15 @@ export interface EventHandlers {
rateLimit?: (data: RateLimitData) => unknown; rateLimit?: (data: RateLimitData) => unknown;
/** Sent when a new Slash Command is created, relevant to the current user. */ /** Sent when a new Slash Command is created, relevant to the current user. */
applicationCommandCreate?: ( applicationCommandCreate?: (
data: Camelize<ApplicationCommandEvent>, data: CamelCaseProps<ApplicationCommandEvent>,
) => unknown; ) => unknown;
/** Sent when a Slash Command relevant to the current user is updated. */ /** Sent when a Slash Command relevant to the current user is updated. */
applicationCommandUpdate?: ( applicationCommandUpdate?: (
data: Camelize<ApplicationCommandEvent>, data: CamelCaseProps<ApplicationCommandEvent>,
) => unknown; ) => unknown;
/** Sent when a Slash Command relevant to the current user is deleted. */ /** Sent when a Slash Command relevant to the current user is deleted. */
applicationCommandDelete?: ( applicationCommandDelete?: (
data: Camelize<ApplicationCommandEvent>, data: CamelCaseProps<ApplicationCommandEvent>,
) => unknown; ) => unknown;
/** Sent when properties about the user change. */ /** Sent when properties about the user change. */
botUpdate?: (user: UserPayload) => unknown; botUpdate?: (user: UserPayload) => unknown;
@@ -242,15 +242,21 @@ export interface EventHandlers {
/** Sent when a member has passed the guild's Membership Screening requirements */ /** Sent when a member has passed the guild's Membership Screening requirements */
membershipScreeningPassed?: (guild: Guild, member: Member) => unknown; membershipScreeningPassed?: (guild: Guild, member: Member) => unknown;
/** Sent when an integration is created on a server such as twitch, youtube etc.. */ /** Sent when an integration is created on a server such as twitch, youtube etc.. */
integrationCreate?: (data: Camelize<IntegrationCreateUpdateEvent>) => unknown; integrationCreate?: (
data: CamelCaseProps<IntegrationCreateUpdateEvent>,
) => unknown;
/** Sent when an integration is updated. */ /** Sent when an integration is updated. */
integrationUpdate?: (data: Camelize<IntegrationCreateUpdateEvent>) => unknown; integrationUpdate?: (
data: CamelCaseProps<IntegrationCreateUpdateEvent>,
) => unknown;
/** Sent when an integration is deleted. */ /** Sent when an integration is deleted. */
integrationDelete?: (data: Camelize<IntegrationDeleteEvent>) => undefined; integrationDelete?: (
data: CamelCaseProps<IntegrationDeleteEvent>,
) => undefined;
/** Sent when a new invite to a channel is created. */ /** Sent when a new invite to a channel is created. */
inviteCreate?: (data: Camelize<InviteCreateEvent>) => unknown; inviteCreate?: (data: CamelCaseProps<InviteCreateEvent>) => unknown;
/** Sent when an invite is deleted. */ /** Sent when an invite is deleted. */
inviteDelete?: (data: Camelize<InviteDeleteEvent>) => unknown; inviteDelete?: (data: CamelCaseProps<InviteDeleteEvent>) => unknown;
} }
/** https://discord.com/developers/docs/topics/gateway#list-of-intents */ /** https://discord.com/developers/docs/topics/gateway#list-of-intents */
+116 -9
View File
@@ -1,10 +1,117 @@
export type CamelizeString<T extends PropertyKey> = T extends string type UpperCaseCharacters =
? string extends T ? string | "A"
: T extends `${infer F}_${infer R}` | "B"
? `${F}${T extends `${infer F}_id` ? Uppercase<R> | "C"
: Capitalize<CamelizeString<R>>}` | "D"
: T | "E"
: T; | "F"
| "G"
| "H"
| "I"
| "J"
| "K"
| "L"
| "M"
| "N"
| "O"
| "P"
| "Q"
| "R"
| "S"
| "T"
| "U"
| "V"
| "W"
| "X"
| "Y"
| "Z";
// deno-fmt-ignore type WordSeparators = "-" | "_" | " ";
export type Camelize<T> = { [K in keyof T as CamelizeString<K>]: T[K] };
type SplitIncludingDelimiters<
Source extends string,
Delimiter extends string,
> = Source extends "" ? []
: Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}` ? (
Source extends `${FirstPart}${infer UsedDelimiter}${SecondPart}`
? UsedDelimiter extends Delimiter
? Source extends `${infer FirstPart}${UsedDelimiter}${infer SecondPart}`
? [
...SplitIncludingDelimiters<FirstPart, Delimiter>,
UsedDelimiter,
...SplitIncludingDelimiters<SecondPart, Delimiter>,
]
: never
: never
: never
)
: [Source];
type StringPartToDelimiterCase<
StringPart extends string,
UsedWordSeparators extends string,
UsedUpperCaseCharacters extends string,
Delimiter extends string,
> = StringPart extends UsedWordSeparators ? Delimiter
: StringPart extends UsedUpperCaseCharacters
? `${Delimiter}${Lowercase<StringPart>}`
: StringPart;
type StringArrayToDelimiterCase<
Parts extends any[],
UsedWordSeparators extends string,
UsedUpperCaseCharacters extends string,
Delimiter extends string,
> = Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
? `${StringPartToDelimiterCase<
FirstPart,
UsedWordSeparators,
UsedUpperCaseCharacters,
Delimiter
>}${StringArrayToDelimiterCase<
RemainingParts,
UsedWordSeparators,
UsedUpperCaseCharacters,
Delimiter
>}`
: "";
type DelimiterCase<Value, Delimiter extends string> = Value extends string
? StringArrayToDelimiterCase<
SplitIncludingDelimiters<Value, WordSeparators | UpperCaseCharacters>,
WordSeparators,
UpperCaseCharacters,
Delimiter
>
: Value;
type InnerCamelCaseStringArray<Parts extends any[], PreviousPart> =
Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
? FirstPart extends undefined ? ""
: FirstPart extends ""
? InnerCamelCaseStringArray<RemainingParts, PreviousPart>
: `${PreviousPart extends "" ? FirstPart
: Capitalize<FirstPart>}${InnerCamelCaseStringArray<
RemainingParts,
FirstPart
>}`
: "";
type CamelCaseStringArray<Parts extends string[]> = Parts extends
[`${infer FirstPart}`, ...infer RemainingParts] ? Uncapitalize<
`${FirstPart}${InnerCamelCaseStringArray<RemainingParts, FirstPart>}`
>
: never;
type Split<S extends string, D extends string> = string extends S ? string[]
: S extends "" ? []
: S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>]
: [S];
export type SnakeCase<Value> = DelimiterCase<Value, "_">;
export type CamelCase<K> = K extends string
? CamelCaseStringArray<Split<K, WordSeparators>>
: K;
export type CamelCaseProps<T> = {
[K in keyof T as CamelCase<K>]: T[K];
};
export type SnakeCaseProps<T> = {
[K in keyof T as SnakeCase<K>]: T[K];
};