A
arktypeβ€’5mo ago
Marvin

Framework-level onDeepUndeclaredKey

Hi, thanks for this awesome library! I'm implementing arktype for my team and I plan on using it for DTOs which would validate the input and the output of our tRPC functions. For the output DTOs, I want to enforce the deletion of "deep undeclared keys" to make sure we don't leak any information, like this:
type({ id: 'string' }).onDeepUndeclaredKey('delete')
type({ id: 'string' }).onDeepUndeclaredKey('delete')
While it works great, I'd like to enforce this at the framework-level, to make sure we never forget to add this onDeepUndeclaredKey('delete'). I thought about doing a dto function wrapper, like this:
function dto<const def, r = Type<type.infer<def>>>(def: type.validate<def>): r {
return type<def, r>(def).onDeepUndeclaredKey('delete')
}
function dto<const def, r = Type<type.infer<def>>>(def: type.validate<def>): r {
return type<def, r>(def).onDeepUndeclaredKey('delete')
}
But I have a Property 'onDeepUndeclaredKey' does not exist on type 'instantiateType<inferDefinition<def, {}, bindThis<def>>, {}>'. error. However, doing dto().onDeepUndeclaredKey('delete') works, for some reason. I could // @ts-expect-error but I'd rather have a clean implementation πŸ˜‰ Do you have any idea or alternative to achieve what I'm trying to? I think this is a fairly common use-case and a clean solution and docs would be great. Thanks!
5 Replies
ssalbdivad
ssalbdivadβ€’5mo ago
Hey thank you! Yes, onUndeclaredKey is actually globally configurable:
// make sure you import from this entry point
// and call configure before the main one
import { configure } from "arktype/config"

configure({
onUndeclaredKey: "delete"
})

import { type } from "arktype"
// make sure you import from this entry point
// and call configure before the main one
import { configure } from "arktype/config"

configure({
onUndeclaredKey: "delete"
})

import { type } from "arktype"
In this particular case, it probably doesn't matter if you do it this way or not- you could call configure anywhere in your initialization logic. It's just a best practice to do it that way because other options like jitless would affect our internal types as well and should be set before the primary arktype import is resolved.
Marvin
MarvinOPβ€’5mo ago
I’m genuinely impressed by how thoroughly this library is designed. Kudos, honestly. It works for me, thanks for the solution. However, I think it would be nice to be able to configure these settings on a per instance basis (type.configure which would return a type function). Therefore we could have these global settings, overridden by the type function instance settings, overridden by the final leaf type settings. In my case I don’t see why I would want undeclared keys but it would feel more robust not to mess with globals πŸ˜‰ Thanks again, and if my suggestion makes sense, I’ll go ahead and try to do a PR, let me know
ssalbdivad
ssalbdivadβ€’5mo ago
This is kind of how it works already. type is a function defined on a Scope.
const $ = scope({ myKeyword: "string" }, { onUndeclaredKey: "delete" })

$.type({ foo: "myKeyword" })
const $ = scope({ myKeyword: "string" }, { onUndeclaredKey: "delete" })

$.type({ foo: "myKeyword" })
You can add your own keywords if you want but you don't have to
Marvin
MarvinOPβ€’5mo ago
Alright, that's... Perfect! Thanks a lot, I'll try to convince my company to sponsor you, ArkType is a game changer.
ssalbdivad
ssalbdivadβ€’5mo ago
I love getting questions about things I already have a good solution for 😍 And thank you! Any support would make a big difference, and regardless I appreciate the thoughtfulness

Did you find this page helpful?