mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-03 23:12:20 +02:00
WIP
This commit is contained in:
parent
903c86a9f6
commit
acf57b7a57
@ -3,7 +3,7 @@ export type EnumOrObject = Record<string | number, string | number>;
|
||||
|
||||
/**
|
||||
* Utility type to extract the enum values from a `const object`,
|
||||
* or convert an `enum` object produced by `typeof Enum` into the union type representing its values.
|
||||
* or convert an `enum` interface produced by `typeof Enum` into the union type representing its values.
|
||||
*/
|
||||
export type EnumValues<E> = E[keyof E];
|
||||
|
||||
@ -12,43 +12,7 @@ export type EnumValues<E> = E[keyof E];
|
||||
* @example
|
||||
* TSNumericEnum<typeof WeatherType>
|
||||
*/
|
||||
// NB: this works because `EnumValues<typeof Enum>` returns the underlying Enum union type.
|
||||
export type TSNumericEnum<T extends EnumOrObject> = number extends EnumValues<T> ? T : never;
|
||||
|
||||
/** Generic type constraint representing a non reverse-mapped TS enum or `const object`. */
|
||||
export type NormalEnum<T extends EnumOrObject> = Exclude<T, TSNumericEnum<T>>;
|
||||
|
||||
// ### Type check tests
|
||||
|
||||
enum testEnumNum {
|
||||
testN1 = 1,
|
||||
testN2 = 2,
|
||||
}
|
||||
|
||||
enum testEnumString {
|
||||
testS1 = "apple",
|
||||
testS2 = "banana",
|
||||
}
|
||||
|
||||
const testObjNum = { testON1: 1, testON2: 2 } as const;
|
||||
|
||||
const testObjString = { testOS1: "apple", testOS2: "banana" } as const;
|
||||
|
||||
testEnumNum satisfies EnumOrObject;
|
||||
testEnumString satisfies EnumOrObject;
|
||||
testObjNum satisfies EnumOrObject;
|
||||
testObjString satisfies EnumOrObject;
|
||||
|
||||
// @ts-expect-error - This is intentionally supposed to fail as an example
|
||||
testEnumNum satisfies NormalEnum<typeof testEnumNum>;
|
||||
testEnumString satisfies NormalEnum<typeof testEnumString>;
|
||||
testObjNum satisfies NormalEnum<typeof testObjNum>;
|
||||
testObjString satisfies NormalEnum<typeof testObjString>;
|
||||
|
||||
testEnumNum satisfies TSNumericEnum<typeof testEnumNum>;
|
||||
// @ts-expect-error - This is intentionally supposed to fail as an example
|
||||
testEnumString satisfies TSNumericEnum<typeof testEnumString>;
|
||||
// @ts-expect-error - This is intentionally supposed to fail as an example
|
||||
testObjNum satisfies TSNumericEnum<typeof testObjNum>;
|
||||
// @ts-expect-error - This is intentionally supposed to fail as an example
|
||||
testObjString satisfies TSNumericEnum<typeof testObjString>;
|
||||
|
@ -38,7 +38,7 @@ export function getEnumKeys<E extends EnumOrObject>(enumType: TSNumericEnum<E>):
|
||||
* @remarks
|
||||
* To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.values} instead.
|
||||
*/
|
||||
// NB: This does not use `EnumValues<E>` as it messes with variable highlighting in IDEs.
|
||||
// NB: This intentionally does not use `EnumValues<E>` as using `E[keyof E]` leads to improved variable highlighting in IDEs.
|
||||
export function getEnumValues<E extends EnumOrObject>(enumType: TSNumericEnum<E>): E[keyof E][] {
|
||||
return Object.values(enumType).filter(v => typeof v !== "string") as E[keyof E][];
|
||||
}
|
||||
@ -59,7 +59,10 @@ export function getEnumValues<E extends EnumOrObject>(enumType: TSNumericEnum<E>
|
||||
* @remarks
|
||||
* If multiple keys map to the same value, the first one (in insertion order) will be retrieved.
|
||||
*/
|
||||
export function enumValueToKey<T extends EnumOrObject>(object: NormalEnum<T>, val: EnumValues<T>): keyof T {
|
||||
export function enumValueToKey<T extends EnumOrObject, V extends EnumValues<T>>(
|
||||
object: NormalEnum<T>,
|
||||
val: V,
|
||||
): keyof T {
|
||||
for (const [key, value] of Object.entries(object)) {
|
||||
if (val === value) {
|
||||
return key;
|
||||
|
101
test/types/enum-types.test-d.ts
Normal file
101
test/types/enum-types.test-d.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type { EnumOrObject, EnumValues, TSNumericEnum, NormalEnum } from "#app/@types/enum-types";
|
||||
|
||||
import type { getEnumKeys, getEnumValues } from "#app/utils/enums";
|
||||
import { enumValueToKey } from "#app/utils/enums";
|
||||
|
||||
import { expectTypeOf, describe, it } from "vitest";
|
||||
|
||||
enum testEnumNum {
|
||||
testN1 = 1,
|
||||
testN2 = 2,
|
||||
}
|
||||
|
||||
enum testEnumString {
|
||||
testS1 = "apple",
|
||||
testS2 = "banana",
|
||||
}
|
||||
|
||||
const testObjNum = { testON1: 1, testON2: 2 } as const;
|
||||
|
||||
const testObjString = { testOS1: "apple", testOS2: "banana" } as const;
|
||||
|
||||
describe("Enum Type Helpers", () => {
|
||||
describe("EnumValues", () => {
|
||||
it("should go from enum object type to value type", () => {
|
||||
expectTypeOf<EnumValues<typeof testEnumNum>>().toEqualTypeOf<testEnumNum>();
|
||||
expectTypeOf<EnumValues<typeof testEnumNum>>().branded.toEqualTypeOf<1 | 2>();
|
||||
|
||||
expectTypeOf<EnumValues<typeof testEnumString>>().toEqualTypeOf<testEnumString>();
|
||||
expectTypeOf<EnumValues<typeof testEnumString>>().toEqualTypeOf<testEnumString.testS1 | testEnumString.testS2>();
|
||||
expectTypeOf<EnumValues<typeof testEnumString>>().toMatchTypeOf<"apple" | "banana">();
|
||||
});
|
||||
|
||||
it("should produce union of const object values as type", () => {
|
||||
expectTypeOf<EnumValues<typeof testObjNum>>().toEqualTypeOf<1 | 2>();
|
||||
|
||||
expectTypeOf<EnumValues<typeof testObjString>>().toEqualTypeOf<"apple" | "banana">();
|
||||
});
|
||||
});
|
||||
|
||||
describe("TSNumericEnum", () => {
|
||||
it("should match numeric enums", () => {
|
||||
expectTypeOf<TSNumericEnum<typeof testEnumNum>>().toEqualTypeOf<typeof testEnumNum>();
|
||||
});
|
||||
|
||||
it("should not match string enums or const objects", () => {
|
||||
expectTypeOf<TSNumericEnum<typeof testEnumString>>().toBeNever();
|
||||
expectTypeOf<TSNumericEnum<typeof testObjNum>>().toBeNever();
|
||||
expectTypeOf<TSNumericEnum<typeof testObjString>>().toBeNever();
|
||||
});
|
||||
});
|
||||
|
||||
describe("NormalEnum", () => {
|
||||
it("should match string enums or const objects", () => {
|
||||
expectTypeOf<NormalEnum<typeof testEnumString>>().toEqualTypeOf<typeof testEnumString>();
|
||||
expectTypeOf<NormalEnum<typeof testObjNum>>().toEqualTypeOf<typeof testObjNum>();
|
||||
expectTypeOf<NormalEnum<typeof testObjString>>().toEqualTypeOf<typeof testObjString>();
|
||||
});
|
||||
it("should not match numeric enums", () => {
|
||||
expectTypeOf<NormalEnum<typeof testEnumNum>>().toBeNever();
|
||||
});
|
||||
});
|
||||
|
||||
describe("EnumOrObject", () => {
|
||||
it("should match any enum or const object", () => {
|
||||
expectTypeOf<typeof testEnumNum>().toMatchTypeOf<EnumOrObject>();
|
||||
expectTypeOf<typeof testEnumString>().toMatchTypeOf<EnumOrObject>();
|
||||
expectTypeOf<typeof testObjNum>().toMatchTypeOf<EnumOrObject>();
|
||||
expectTypeOf<typeof testObjString>().toMatchTypeOf<EnumOrObject>();
|
||||
});
|
||||
|
||||
it("should not match an enum value union w/o typeof", () => {
|
||||
expectTypeOf<testEnumNum>().not.toMatchTypeOf<EnumOrObject>();
|
||||
expectTypeOf<testEnumString>().not.toMatchTypeOf<EnumOrObject>();
|
||||
});
|
||||
|
||||
it("should be equivalent to `TSNumericEnum | NormalEnum`", () => {
|
||||
expectTypeOf<EnumOrObject>().branded.toEqualTypeOf<TSNumericEnum<EnumOrObject> | NormalEnum<EnumOrObject>>();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Enum Functions", () => {
|
||||
describe("getEnumKeys", () => {
|
||||
it("should retrieve keys of numeric enum", () => {
|
||||
expectTypeOf<typeof getEnumKeys<typeof testEnumNum>>().returns.toEqualTypeOf<("testN1" | "testN2")[]>();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getEnumValues", () => {
|
||||
it("should retrieve values of numeric enum", () => {
|
||||
expectTypeOf<typeof getEnumValues<typeof testEnumNum>>().returns.branded.toEqualTypeOf<(1 | 2)[]>();
|
||||
});
|
||||
});
|
||||
|
||||
describe("enumValueToKey", () => {
|
||||
it("should retrieve values for a given key", () => {
|
||||
// @ts-expect-error oopsie
|
||||
expectTypeOf(enumValueToKey(testEnumString, testEnumString.testS1)).toEqualTypeOf<"testS1">();
|
||||
});
|
||||
});
|
||||
});
|
@ -28,19 +28,23 @@ export default defineProject(({ mode }) => ({
|
||||
}
|
||||
},
|
||||
},
|
||||
environment: "jsdom" as const,
|
||||
environment: "jsdom",
|
||||
environmentOptions: {
|
||||
jsdom: {
|
||||
resources: "usable",
|
||||
},
|
||||
},
|
||||
typecheck: {
|
||||
tsconfig: "tsconfig.json",
|
||||
include: ["./test/types/**/*.{test,spec}{-|.}d.ts"],
|
||||
},
|
||||
threads: false,
|
||||
trace: true,
|
||||
restoreMocks: true,
|
||||
watch: false,
|
||||
coverage: {
|
||||
provider: "istanbul" as const,
|
||||
reportsDirectory: "coverage" as const,
|
||||
provider: "istanbul",
|
||||
reportsDirectory: "coverage",
|
||||
reporters: ["text-summary", "html"],
|
||||
},
|
||||
name: "main",
|
||||
|
Loading…
Reference in New Issue
Block a user