mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-04 07:22:19 +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`,
|
* 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];
|
export type EnumValues<E> = E[keyof E];
|
||||||
|
|
||||||
@ -12,43 +12,7 @@ export type EnumValues<E> = E[keyof E];
|
|||||||
* @example
|
* @example
|
||||||
* TSNumericEnum<typeof WeatherType>
|
* 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;
|
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`. */
|
/** Generic type constraint representing a non reverse-mapped TS enum or `const object`. */
|
||||||
export type NormalEnum<T extends EnumOrObject> = Exclude<T, TSNumericEnum<T>>;
|
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
|
* @remarks
|
||||||
* To retrieve the keys of a {@linkcode NormalEnum}, use {@linkcode Object.values} instead.
|
* 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][] {
|
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][];
|
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
|
* @remarks
|
||||||
* If multiple keys map to the same value, the first one (in insertion order) will be retrieved.
|
* 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)) {
|
for (const [key, value] of Object.entries(object)) {
|
||||||
if (val === value) {
|
if (val === value) {
|
||||||
return key;
|
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: {
|
environmentOptions: {
|
||||||
jsdom: {
|
jsdom: {
|
||||||
resources: "usable",
|
resources: "usable",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
typecheck: {
|
||||||
|
tsconfig: "tsconfig.json",
|
||||||
|
include: ["./test/types/**/*.{test,spec}{-|.}d.ts"],
|
||||||
|
},
|
||||||
threads: false,
|
threads: false,
|
||||||
trace: true,
|
trace: true,
|
||||||
restoreMocks: true,
|
restoreMocks: true,
|
||||||
watch: false,
|
watch: false,
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "istanbul" as const,
|
provider: "istanbul",
|
||||||
reportsDirectory: "coverage" as const,
|
reportsDirectory: "coverage",
|
||||||
reporters: ["text-summary", "html"],
|
reporters: ["text-summary", "html"],
|
||||||
},
|
},
|
||||||
name: "main",
|
name: "main",
|
||||||
|
Loading…
Reference in New Issue
Block a user