I’m trying to translate some TypeScript type definitions into ArkType’s syntax, but I’m hitting a wall. Specifically, I’m trying to replicate TypeScript’s template literal types (ex application/${keyof typeof application}
) in ArkType, but I’m not sure if I’m using the right approach or if ArkType even supports this kind of type composition.
Here’s what can do very elegantly in TypeScript:
const text = {
html: ['html'],
markdown: ['md'],
xml: ['xml'],
plain: ['txt', 'text', 'sh'],
csv: ['csv'],
css: ['css'],
javascript: ['js'],
};
const video = {
wmv: ['wmv'],
mp4: ['mp4'],
mpeg: ['mpeg'],
matroska: ['mkv'],
};
const audio = {
mp4: ['mp4'],
mp3: ['mp3'],
mpeg: ['mpeg'],
};
const image = {
png: ['png'],
jpeg: ['jpg', 'jpeg'],
avif: ['avif'],
AV1: ['avi'],
webp: ['webp'],
};
const application = {
json: ['json'],
yaml: ['yaml', 'yml'],
};
export type ApplicationType = `application/${keyof typeof application}`;
export type ImageType = `image/${keyof typeof image}`;
export type VideoType = `video/${keyof typeof video}`;
export type AudioType = `audio/${keyof typeof audio}`;
export type TextType = `text/${keyof typeof text}`;
export type MimeType = ApplicationType | ImageType | VideoType | AudioType | TextType;
However I can't seem to translate this approach into the syntax ArkType will parse.
I've Naively tried this approach with a few different variations for the MediaTypes, but I am not having much success.
export const { MimeType } = type.module({
// Subtype unions for each MediaType
ApplicationSub: "'json' | 'yaml'",
ImageSub: "'png' | 'jpeg' | 'avif' | 'AV1' | 'webp'",
VideoSub: "'wmv' | 'mp4' | 'mpeg' | 'matroska'",
AudioSub: "'mp4' | 'mp3' | 'mpeg'",
TextSub: "'html' | 'markdown' | 'xml' | 'plain' | 'csv' | 'css' | 'javascript'",
// MediaTypes with their extensions
Application: "`application/${ApplicationSub}`", // <-- Type '"`application/${ApplicationSub}`"' is not assignable to type '"'`application/${ApplicationSub}`' is unresolvable "'.
Image: "'image/'ImageSub", // <-- Type '"'image/'ImageSub"' is not assignable to type '"'I' is not allowed here "'.ts(2322)
Video: "\'video/\'VideoSub", // <--Type '"'video/'VideoSub"' is not assignable to type '"'V' is not allowed here "'.
Audio: "audio/${AudioSub}", // <-- Type '"audio/${AudioSub}"' is not assignable to type '"'audio/${AudioSub}' is unresolvable "'.
Text: "text/${TextSub}",
// Final union type
MimeType: "Application | Image | Video | Audio | Text"
});
Can anyone point me in the right direction for expressing string template types with ArkType? or is not possible?
Thanks!
Thank you for asking this question, Wayne Griffiths.
Apologies for not explaining the answer and just pasting the suggested code right away, dbc. Thanks for letting me know since it is my first time answering a question in stackoverflow.
I've been using ArkType for only a few days now and I also encountered this same problem. It was a struggle since the examples for the counterpart of string literals in the ArkType Documentation are quite limited. But I came up with this answer by sticking with their tagline, "TypeScript's 1:1 validator, optimized from editor to runtime" and trying things out with hunch. The magic (for lack of term and deep understanding of the library) happens with the .infer
which seems to give us the inferred string literals and not the Type<T>
that is being returned by the ArkType's type()
function.
I hope this might help.
import { type } from "arktype";
const ApplicationSub = type("'json' | 'yaml'");
const Application = type(`"application/${ApplicationSub.infer}"`);
const ImageSub = type("'png' | 'jpeg' | 'avif' | 'AV1' | 'webp'");
const Image = type(`"image/${ImageSub.infer}"`);
const VideoSub = type("'wmv' | 'mp4' | 'mpeg' | 'matroska'");
const Video = type(`"video/${VideoSub.infer}"`);
const AudioSub = type("'mp4' | 'mp3' | 'mpeg'");
const Audio = type(`"audio/${AudioSub.infer}"`);
const TextSub = type(
"'html' | 'markdown' | 'xml' | 'plain' | 'csv' | 'css' | 'javascript'",
);
const Text = type(`"text/${TextSub.infer}"`);
const MimeType = type(
`"${Application.infer}" | "${Image.infer}" | "${Video.infer}" | "${Audio.infer}" | "${Text.infer}"`,
);