Skip to content

Instantly share code, notes, and snippets.

@izakfilmalter
Created June 13, 2025 12:14
Show Gist options
  • Save izakfilmalter/193ee754606a238585f7773bd1711c89 to your computer and use it in GitHub Desktop.
Save izakfilmalter/193ee754606a238585f7773bd1711c89 to your computer and use it in GitHub Desktop.
const BaseResponseSchema = Schema.Struct({
included: Schema.Array(
Schema.Union(
Schema.Struct({
type: Schema.Literal("Person"),
foo: Schema.String
}),
Schema.Struct({
type: Schema.Literal("Address"),
yo: Schema.String
}),
Schema.Struct({
type: Schema.Literal("Email"),
yeet: Schema.String
})
)
)
})
// Use the runtime-based extraction that actually works
type ExtractLiteralFromSchema<T> = T extends {
fields: {
type: {
literals: readonly [infer U, ...any[]]
}
}
} ? U :
never
type MemberKeys = ExtractLiteralFromSchema<typeof BaseResponseSchema.fields.included.value.members[number]>
// This should now be "Person" | "Address" | "Email"
// Create the mapped type
type ExtractedMembers = {
[K in MemberKeys]: Extract<
typeof BaseResponseSchema.fields.included.value.members[number],
{ fields: { type: { literals: readonly [K, ...any[]] } } }
>
}
// Runtime helper
function extractMembersFromUnion(
unionMembers: typeof BaseResponseSchema.fields.included.value.members
): ExtractedMembers {
const result = {} as any
unionMembers.forEach((member) => {
const typeValue = member.fields.type.literals[0] as MemberKeys
result[typeValue] = member
})
return result
}
const members = extractMembersFromUnion(BaseResponseSchema.fields.included.value.members)
// Now your pickMembers should work
function pickMembers<const T extends ReadonlyArray<MemberKeys>>(
ts: T
): T extends readonly [] ? Schema.Tuple<[]>
: Schema.Union<{ readonly [K in keyof T]: ExtractedMembers[T[K]] }>
{
if (ts.length === 0) {
// Return a schema that will never match anything
return Schema.Never as any
}
return Schema.Union(...ts.map((t) => members[t])) as any
}
// Usage
const result = Schema.Struct({
included: Schema.Array(
pickMembers(["Address", "Person"])
)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment