A
arktype6mo ago
francis

Are there examples of how to create mapped types?

e.g. a { [K in <SomeOtherType>]: SomeValueType } type, where SomeValueType is either a static type or is computed from K. I currently have a very basic setup where I have:
type MappedEnum<T extends string> = { readonly [K in T]: T };

const kitTypes = ["test_only", "test_self_serve", "test_guided_audit"];
const kitTypeValidator = type("===", ...kitTypes);
export type KitType = typeof kitTypeValidator.infer;
export const KitType = {
test_only: "test_only",
test_self_serve: "test_self_serve",
test_guided_audit: "test_guided_audit",
} as const satisfies MappedEnum<KitType>;
type MappedEnum<T extends string> = { readonly [K in T]: T };

const kitTypes = ["test_only", "test_self_serve", "test_guided_audit"];
const kitTypeValidator = type("===", ...kitTypes);
export type KitType = typeof kitTypeValidator.infer;
export const KitType = {
test_only: "test_only",
test_self_serve: "test_self_serve",
test_guided_audit: "test_guided_audit",
} as const satisfies MappedEnum<KitType>;
This pattern allows me to generate, from a string array, a validator for an element in that array, a type for an element of the resulting string union type, and a type for a runtime object that lets me access the values analogous to an enum. And it's great! My question is, I have the following type definition:
type KitCounts = { [K in KitType]?: number };
type KitCounts = { [K in KitType]?: number };
Is there a way to write an ArkType function that will generate a validator for this type (and the type definition itself), given the kitTypes array? My initial attempt is:
const kitCountsValidator = type(
Object.fromEntries(kitType.enumValues.map((k) => [`${k}?`, "number"])),
);
const kitCountsValidator = type(
Object.fromEntries(kitType.enumValues.map((k) => [`${k}?`, "number"])),
);
Obviously, this doesn't work, since the type information is completely lost. It actually works fine at runtime but the type is useless. Is there a way to do this without writing it out explicitly? (I have 5+ other enums that I'd like to process in a similar way)
12 Replies
francis
francisOP6mo ago
aha, great place to start looking, thank you! immediate follow up question: is there a way to generate the enumerable union from my other union arktype instance? i.e. is there a way to generate "'test_only' | 'test_self_serve' | 'test_guided_audit'" from my kitTypeValidator - and if so, would it then be safe to use a template string literal to put a ? on the end?
ssalbdivad
ssalbdivad6mo ago
If you have an index signature you can't make it optional, but you could refer to your union directly, at least in a scope
francis
francisOP6mo ago
or is there a way to define a Record in arktype without using object syntax? I assume I have to turn it into an intermediate string since only strings / symbols are property keys
ssalbdivad
ssalbdivad6mo ago
ark.Record ark is a module containing all the builtin keywords
francis
francisOP6mo ago
ah, right, thank you. I forgot that index signatures are always optional (or rather, they're weird, and I am not entirely sure I like how typescript does it but I also can't think of a better way) turning on noUncheckedIndexAccess is better for type safety but breaks so many libraries that I gave up on it and define all the records I use internal to our application as Record<key, value | undefined> this is one of the reasons I like mapped types, with mapped types you can enforce that all keys are present
ssalbdivad
ssalbdivad6mo ago
It won't be inferred as optional in that case but you can .partial() it to make it optional Index signatures would enforce all the kys are present if it is an enumerable list
francis
francisOP6mo ago
oh, interesting, so it doesn't behave like a typescript record
ssalbdivad
ssalbdivad6mo ago
(that's how it works in TS so I mirrored that behavior, even though it's a bit weird IMO) It does, if you pass an enumerable union to a Record those keys will be required
francis
francisOP6mo ago
jk, I forgot that part, yeah I must be doing something very wrong but I can't figure out how to get ark.Record to work at all? even following the test case I found in the arktype repo gets me different results.
import { ark } from "arktype";
// const t: Type<Record<Key, unknown>, Ark>
const t = ark.Record("string", "number");
// type T = {
// [x: string]: unknown;
// [x: symbol]: unknown;
// }
type T = typeof t.infer;
import { ark } from "arktype";
// const t: Type<Record<Key, unknown>, Ark>
const t = ark.Record("string", "number");
// type T = {
// [x: string]: unknown;
// [x: symbol]: unknown;
// }
type T = typeof t.infer;
ssalbdivad
ssalbdivad6mo ago
There was a bug with generic inference in some recent versions is it up to date?
francis
francisOP6mo ago
I updated a couple days ago ah, new version, I'll update and check. thanks!

Did you find this page helpful?