A
arktype5mo ago
francis

How would you define an object with optional keys, but require at least one of the keys to be filled

An example of a very basic schema:
const schema = type({ a: type.string.optional(), b: type.string.optional() });
const schema = type({ a: type.string.optional(), b: type.string.optional() });
I'd like this to accept values {a: 'foo'}, { b: 'foo' }, and { a: 'foo', b: 'bar' }, but fail on {}. It would be a lovely bonus for the type to be inferred as { a: string, b?: string } | { a?: string, b: string } as well. Is there a way to do this without explicitly specifying that inferred type as an arktype union using .or?
8 Replies
Dimava
Dimava5mo ago
type(...).narrow((e, ctx) => Object.keys(e).length > 0 || ctx.mustBe('not empty object'))
type(...).narrow((e, ctx) => Object.keys(e).length > 0 || ctx.mustBe('not empty object'))
reminder: the ideomatic definitions in ArkType look like
const schema = type({
'a?': 'string'
'b?': 'string'
});
const schema = type({
'a?': 'string'
'b?': 'string'
});
francis
francisOP5mo ago
this doesn't change the type of the expression, though. A literal type of {} is still assignable to the type of narrowed.infer
Dimava
Dimava5mo ago
import { type } from 'arktype'

type HasKeys<T> = { [K in keyof T]: Required<Pick<T, K>> }[keyof T]

const schema = type({
'a?': 'string',
'b?': 'string',
}).narrow((value, ctx): value is typeof value & HasKeys<typeof value> => {
return Object.keys(value).length > 0 || ctx.mustBe('not empty object');
})

let x: typeof schema.infer = {a: ''}
import { type } from 'arktype'

type HasKeys<T> = { [K in keyof T]: Required<Pick<T, K>> }[keyof T]

const schema = type({
'a?': 'string',
'b?': 'string',
}).narrow((value, ctx): value is typeof value & HasKeys<typeof value> => {
return Object.keys(value).length > 0 || ctx.mustBe('not empty object');
})

let x: typeof schema.infer = {a: ''}
TizzySaurus
TizzySaurus5mo ago
The only native way would be a union type({"a?": "string", b: "string"}).or({"a": "string", "b?": "string"}) But obviously that doesn't scale very well May still be suitable though, depending on your actual use case
francis
francisOP5mo ago
ah yeah. hm
francis
francisOP5mo ago
I was hoping there would be a native operation for this, similar to how https://github.com/sindresorhus/type-fest/blob/main/source/require-at-least-one.d.ts works at the type level
GitHub
type-fest/source/require-at-least-one.d.ts at main · sindresorhus/t...
A collection of essential TypeScript types. Contribute to sindresorhus/type-fest development by creating an account on GitHub.
ssalbdivad
ssalbdivad4mo ago
This is how TypeScript works, so it wouldn't really be appropriate to change it by default. Something like Dimava's solution is your best bet
francis
francisOP4mo ago
ah, I wasn't asking for a default change, to be clear it would be nice to have a 'best practices' way to do this, either added as a function somewhere in the arktype package, or as a documentation resource (the above doesn't work in a world where you allow extra keys, fyi - though I don't actually need this anymore, I found a different solution)

Did you find this page helpful?