Skip to content

Commit

Permalink
test: improve type check accuracy around unions
Browse files Browse the repository at this point in the history
  • Loading branch information
KnorpelSenf committed Oct 28, 2024
1 parent 7c5ffc4 commit 7408120
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 23 deletions.
77 changes: 59 additions & 18 deletions test/composer.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import {
assertType,
beforeEach,
describe,
type Has,
type IsExact,
type IsMutuallyAssignable,
it,
} from "./deps.test.ts";

Expand Down Expand Up @@ -62,7 +62,10 @@ describe("Composer types", () => {
const match = ctx.match;
const chatId = ctx.chatId;
assertType<
Has<typeof msg, MaybeInaccessibleMessage | undefined>
IsMutuallyAssignable<
typeof msg,
MaybeInaccessibleMessage | undefined
>
>(
true,
);
Expand Down Expand Up @@ -133,18 +136,25 @@ describe("Composer types", () => {
const from = ctx.from;
const channelPost = ctx.channelPost;

assertType<Has<typeof chat, Chat.PrivateChat>>(true);
assertType<IsMutuallyAssignable<typeof chat, Chat.PrivateChat>>(
true,
);
assertType<IsExact<typeof chatId, number>>(true);
assertType<IsExact<typeof from, User>>(true);
assertType<IsExact<typeof channelPost, undefined>>(true);
if (ctx.message) {
assertType<Has<typeof ctx.message.chat, Chat.PrivateChat>>(
assertType<
IsMutuallyAssignable<
typeof ctx.message.chat,
Chat.PrivateChat
>
>(
true,
);
}
if (ctx.callbackQuery?.message) {
assertType<
Has<
IsMutuallyAssignable<
typeof ctx.callbackQuery.message.chat,
Chat.PrivateChat
>
Expand All @@ -158,17 +168,24 @@ describe("Composer types", () => {
const chatId = ctx.chatId;
const channelPost = ctx.channelPost;

assertType<Has<typeof chat, Chat.GroupChat>>(true);
assertType<IsMutuallyAssignable<typeof chat, Chat.GroupChat>>(
true,
);
assertType<IsExact<typeof chatId, number>>(true);
assertType<IsExact<typeof channelPost, undefined>>(true);
if (ctx.message) {
assertType<Has<typeof ctx.message.chat, Chat.GroupChat>>(
assertType<
IsMutuallyAssignable<
typeof ctx.message.chat,
Chat.GroupChat
>
>(
true,
);
}
if (ctx.callbackQuery?.message) {
assertType<
Has<
IsMutuallyAssignable<
typeof ctx.callbackQuery.message.chat,
Chat.GroupChat
>
Expand All @@ -182,17 +199,22 @@ describe("Composer types", () => {
const chatId = ctx.chatId;
const channelPost = ctx.channelPost;

assertType<Has<typeof chat, Chat.SupergroupChat>>(true);
assertType<
IsMutuallyAssignable<typeof chat, Chat.SupergroupChat>
>(true);
assertType<IsExact<typeof chatId, number>>(true);
assertType<IsExact<typeof channelPost, undefined>>(true);
if (ctx.message) {
assertType<
Has<typeof ctx.message.chat, Chat.SupergroupChat>
IsMutuallyAssignable<
typeof ctx.message.chat,
Chat.SupergroupChat
>
>(true);
}
if (ctx.callbackQuery?.message) {
assertType<
Has<
IsMutuallyAssignable<
typeof ctx.callbackQuery.message.chat,
Chat.SupergroupChat
>
Expand All @@ -206,17 +228,22 @@ describe("Composer types", () => {
const chatId = ctx.chatId;
const message = ctx.message;

assertType<Has<typeof chat, Chat.ChannelChat>>(true);
assertType<IsMutuallyAssignable<typeof chat, Chat.ChannelChat>>(
true,
);
assertType<IsExact<typeof chatId, number>>(true);
assertType<IsExact<typeof message, undefined>>(true);
if (ctx.channelPost) {
assertType<
Has<typeof ctx.channelPost.chat, Chat.ChannelChat>
IsMutuallyAssignable<
typeof ctx.channelPost.chat,
Chat.ChannelChat
>
>(true);
}
if (ctx.callbackQuery?.message) {
assertType<
Has<
IsMutuallyAssignable<
typeof ctx.callbackQuery.message.chat,
Chat.ChannelChat
>
Expand All @@ -230,17 +257,28 @@ describe("Composer types", () => {
const chatId = ctx.chatId;

assertType<
Has<typeof chat, Chat.PrivateChat | Chat.ChannelChat>
IsMutuallyAssignable<
typeof chat,
Chat.PrivateChat | Chat.ChannelChat
>
>(true);
assertType<IsExact<typeof chatId, number>>(true);
if (ctx.message) {
assertType<Has<typeof ctx.message.chat, Chat.PrivateChat>>(
assertType<
IsMutuallyAssignable<
typeof ctx.message.chat,
Chat.PrivateChat
>
>(
true,
);
}
if (ctx.channelPost) {
assertType<
Has<typeof ctx.channelPost.chat, Chat.ChannelChat>
IsMutuallyAssignable<
typeof ctx.channelPost.chat,
Chat.ChannelChat
>
>(true);
}
});
Expand All @@ -256,7 +294,10 @@ describe("Composer types", () => {
const gameShortName = ctx.callbackQuery.game_short_name;
const match = ctx.match;
assertType<
Has<typeof msg, MaybeInaccessibleMessage | undefined>
IsMutuallyAssignable<
typeof msg,
MaybeInaccessibleMessage | undefined
>
>(
true,
);
Expand Down
13 changes: 9 additions & 4 deletions test/context.type.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Composer, type Context } from "../src/mod.ts";
import {
assertType,
describe,
type Has,
type IsExact,
type IsMutuallyAssignable,
it,
} from "./deps.test.ts";

Expand All @@ -20,11 +20,16 @@ describe("ctx.has* checks", () => {
}
if (ctx.hasText("123")) {
assertType<
Has<typeof ctx.match, string | RegExpMatchArray>
IsMutuallyAssignable<
typeof ctx.match,
string | RegExpMatchArray
>
>(true);
}
if (ctx.hasCommand("123")) {
assertType<Has<typeof ctx.match, string>>(true);
assertType<IsMutuallyAssignable<typeof ctx.match, string>>(
true,
);
}
if (ctx.hasChatType("private")) {
assertType<IsExact<typeof ctx.chat.type, "private">>(true);
Expand All @@ -44,7 +49,7 @@ describe("ctx.has* checks", () => {
}
});
c.command("c", (ctx) => {
assertType<Has<typeof ctx.match, string>>(true);
assertType<IsMutuallyAssignable<typeof ctx.match, string>>(true);
});
});
});
25 changes: 24 additions & 1 deletion test/deps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,31 @@ export {
} from "jsr:@std/assert";
export { afterEach, beforeEach, describe, it } from "jsr:@std/testing/bdd";
export { type Spy, spy, type Stub, stub } from "jsr:@std/testing/mock";
export { assertType, type Has, type IsExact } from "jsr:@std/testing/types";
export { assertType, type IsExact } from "jsr:@std/testing/types";

/**
* Checks if the actual type `A` is assignable to the expected type `E`, and
* vice versa.
*
* ```ts
* // false because E is not assignable to A
* type P = IsMutuallyAssignable<string & RegExpMatchArray, string>;
* // false because A is not assignable to E
* type Q = IsMutuallyAssignable<string | RegExpMatchArray, string>;
* // true
* type R = IsMutuallyAssignable<string | (string & RegExpMatchArray), string>;
* ```
*/
export type IsMutuallyAssignable<A, E> = [E] extends [A]
? [A] extends [E] ? true : false
: false;

/**
* Collects a potentially async iterator of `Uint8Array` objects into a single
* array.
*
* @param data a stream of byte chunks
*/
export async function convertToUint8Array(
data: Iterable<Uint8Array> | AsyncIterable<Uint8Array>,
) {
Expand Down

0 comments on commit 7408120

Please sign in to comment.