Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved typing for union types #2151

Merged
merged 12 commits into from
Mar 1, 2024
Next Next commit
Have unions not devolve into IAnyType with 10+ members.
  • Loading branch information
thegedge committed Feb 24, 2024
commit 02ee34dd7378bc88fa9b5f39eb71daee65f80a38
48 changes: 43 additions & 5 deletions __tests__/core/type-system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,21 @@ type DifferingKeys<ActualT, ExpectedT> = {
}[keyof ActualT | keyof ExpectedT] &
string

type InexactErrorMessage<ActualT, ExpectedT> = `Mismatched property: ${DifferingKeys<
ActualT,
ExpectedT
>}`
type NotExactErrorMessage<ActualT, ExpectedT> = ActualT extends Record<string, unknown>
? ExpectedT extends Record<string, unknown>
? `Mismatched property: ${DifferingKeys<ActualT, ExpectedT>}`
: "Expected a non-object type, but received an object"
: ExpectedT extends Record<string, unknown>
? "Expected an object type, but received a non-object type"
: "Types are not exactly equal"

type IsExact<T1, T2> = [T1] extends [T2] ? ([T2] extends [T1] ? Exact<T1, T2> : never) : never

const assertTypesEqual = <ActualT, ExpectedT>(
t: ActualT,
u: Exact<ActualT, ExpectedT> extends never ? InexactErrorMessage<ActualT, ExpectedT> : ExpectedT
u: IsExact<ActualT, ExpectedT> extends never
? NotExactErrorMessage<ActualT, ExpectedT>
: ExpectedT
): [ActualT, ExpectedT] => [t, u] as [ActualT, ExpectedT]
const _: unknown = undefined

Expand Down Expand Up @@ -1172,3 +1179,34 @@ test("maybe / optional type inference verification", () => {
}
)
})

test("union type inference verification for small number of types", () => {
const T = types.union(types.boolean, types.literal("test"), types.maybe(types.number))

type ITC = SnapshotIn<typeof T>
type ITS = SnapshotOut<typeof T>

assertTypesEqual(_ as ITC, _ as boolean | "test" | number | undefined)
assertTypesEqual(_ as ITS, _ as boolean | "test" | number | undefined)
})

test("union type inference verification for a large number of types", () => {
const T = types.union(
types.literal("a"),
types.literal("b"),
types.literal("c"),
types.literal("d"),
types.literal("e"),
types.literal("f"),
types.literal("g"),
types.literal("h"),
types.literal("i"),
types.literal("j")
)

type ITC = SnapshotIn<typeof T>
type ITS = SnapshotOut<typeof T>

assertTypesEqual(_ as ITC, _ as "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j")
assertTypesEqual(_ as ITS, _ as "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j")
})
5 changes: 4 additions & 1 deletion src/types/utility-types/enumeration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ export function enumeration<T extends string>(
* @param options possible values this enumeration can have
* @returns
*/
export function enumeration(name: string | string[], options?: any): ISimpleType<string> {
export function enumeration<OptionsT extends any[]>(
name: string | string[],
options?: OptionsT
): ISimpleType<string> {
const realOptions: string[] = typeof name === "string" ? options! : name
// check all options
if (devMode()) {
Expand Down
Loading