A
arktype11mo ago
francis

Is there a recommended way to create a Record using a type as a key?

None of these seem to work:
const stringKeys = type(
"===",
"CAR",
"ENDO",
"REP",
"DEV",
"GEN",
"NEU",
"ORG",
"IMM",
"RES",
"IRR",
"ECO",
);

const explicit = type.Record(stringKeys, type("number | null"));

const stringSyntax = type(
'Record<"CAR" | "ENDO" | "REP" | "DEV" | "GEN" | "NEU" | "ORG" | "IMM" | "RES" | "IRR" | "ECO", number | null>',
);
const stringKeys = type(
"===",
"CAR",
"ENDO",
"REP",
"DEV",
"GEN",
"NEU",
"ORG",
"IMM",
"RES",
"IRR",
"ECO",
);

const explicit = type.Record(stringKeys, type("number | null"));

const stringSyntax = type(
'Record<"CAR" | "ENDO" | "REP" | "DEV" | "GEN" | "NEU" | "ORG" | "IMM" | "RES" | "IRR" | "ECO", number | null>',
);
4 Replies
francis
francisOP11mo ago
I get the error ParseError: Index keys "CAR", "DEV", "ECO", "ENDO", "GEN", "IMM", "IRR", "NEU", "ORG", "REP", "RES" should be specified as named props.
ssalbdivad
ssalbdivad11mo ago
Yeah unfortunately this was a mistake in how the parser was implemented to ensure named properties are normalized in their representation: https://github.com/arktypeio/arktype/issues/952 The easiest thing to do for now would probably just be to define that initial set of keys as a tuple, then you can map it with Object.fromEntries to build the type you want.
GitHub
Allow index node to be parsed from enumerable keys · Issue #952 · a...
Currently, an index signature including a literal key like "a" is an error due to some limitations of how the parser is implemented. There is a normalizeIndexKey function, but it is not u...
camelCaseBill
camelCaseBill3mo ago
It is a bit embarrassing that I have to ask - but how does that workaround work? I tried a few things with Object.fromEntries but could never get it to work. I am missing something obvious here, sorry. Does anyone have a working example for something like Record<"A" | "B", number | null>?
ssalbdivad
ssalbdivad3mo ago
Something like this should do what you want:
import { type } from "arktype"

const keys = ["A", "B"] as const

const arkKeysWithValue = <keys extends readonly string[], def>(
keys: keys,
def: type.validate<def>
): type<{ [k in keys[number]]: type.infer<def> }> => {
const valueType = type.raw(def)
return type.raw(Object.fromEntries(keys.map(k => [k, valueType]))) as never
}

// Type<{ A: number | null; B: number | null }>
const t = arkKeysWithValue(keys, "null | number")

// { A: number | null, B: number | null }
console.log(t.expression)
import { type } from "arktype"

const keys = ["A", "B"] as const

const arkKeysWithValue = <keys extends readonly string[], def>(
keys: keys,
def: type.validate<def>
): type<{ [k in keys[number]]: type.infer<def> }> => {
const valueType = type.raw(def)
return type.raw(Object.fromEntries(keys.map(k => [k, valueType]))) as never
}

// Type<{ A: number | null; B: number | null }>
const t = arkKeysWithValue(keys, "null | number")

// { A: number | null, B: number | null }
console.log(t.expression)

Did you find this page helpful?