Is possible to use array values resulted from `string.match()` as options for a ts type?

Viewed 45

Hey,

I think it may be useful to start by providing all the packages:

    "@figma/plugin-typings": "^1.17.1",
    "@rollup/plugin-json": "^4.1.0",
    "@rollup/plugin-node-resolve": "^13.0.4",
    "@rollup/plugin-typescript": "^8.2.5",
    "prettier": "^2.3.2",
    "prettier-plugin-organize-imports": "^2.3.3",
    "rollup": "^2.32.0",
    "rollup-plugin-copy": "^3.4.0",
    "rollup-plugin-terser": "^7.0.2",
    "tslib": "^2.1.0",
    "typescript": "^4.2.4"

I have a .json file which from which I want to extract certain information.

// file.json
{
   ...,
   {
      "name": "Copy Selection",
      "command": "copySelection"
    },
    {
      "name": "Paste In",
      "command": "pasteIn"
    },
    ...

}

I'm trying to get all the commands from the file.json and use them as type options:

import manifest from '../manifest.json';

const regex = /(?<="command":")(.*?)(?=")/g;

const matches = JSON.stringify(manifest).match(regex);
const commands = [...matches] as const;

export type PluginCommands = typeof commands[number];
// results
// PluginCommands = string

// expected
// PluginCommands = "copySelection" | "PasteIn" | ...

const meh = ['ala', 'bala', 'portcolaa'] as const;

type mehType = typeof meh[number];
// mehType = "ala" | "bala" | "portcolaa"

I want this because I want to get use of vscode intellisense and autocomplete, and ts warnings.

While writing this question, an idea stroke myself, to write a rollup plugin to get the matches into a file.

āœ… My solution:

I used the rollup-plugin-copy to create a modified copy of the json file.

Then I'm using variable as source for my type.

Below is the code I used:

// rollup.config
     ...
     copy({
        targets: [{ src: './src/manifest.json', dest: './build' }],
        targets: [
          {
            src: './src/manifest.json',
            dest: './src/',
            rename: 'manifestCommands.ts',
            transform: (contents, filename) => {
              const content = contents.toString();

              const regex = /(?<="command": ")(.*?)(?=")/g;
              const matches = content.match(regex);

              const results = `export const manifestCommands = ${JSON.stringify(matches)} as const`;

              return results;
            },
          },
        ],
      })
      ...
// another_file.ts
// import manifestCommands variable

type PluginCommands = typeof manifestCommands[number];
1 Answers

Importing a JSON file will give you it's structure as a type, but it won't ever give you anything more specific than a string, so this isn't possible. JSON imports just aren't typed that strongly.

Also, match will only ever return strings, so that's not possible either.


To pull this off you'd need to treat your object in that JSON like source code, and not data. Which means you make it a typescript file

// manifest.ts
const manifest = [
    {
        "name": "Copy Selection",
        "command": "copySelection"
    },
    {
        "name": "Paste In",
        "command": "pasteIn"
    },
] as const

Then you can simply do:

type PluginCommands = typeof manifest[number]['command']
// "copySelection" | "pasteIn"

Playground


But if your file must be JSON, then this isn't ever going to work.

Lastly, I wouldn't ever recommend regexing through a JSON string for anything. It's always going to better to parse that JSON string and then crawl through the resulting values.

Related