Using Typescript generics to customize parsing grammars: a comprehensive guide

I am looking to create a parser in Typescript that can handle symbols specific to language grammars.

Initially, I attempted to parameterize the Sym type for symbols that vary depending on the language grammar. However, I encountered a type checking error in the first line of Parser.js:

Type Sym does not meet the constraint string | number | symbol.

I tried various ways to adjust the definition of GrammarRules<Sym> to fix this issue, but each solution led to additional problems downstream, some of which were difficult to resolve – a comprehensive solution eluded me.

How can I redefine the symbol type, Sym, for each parser language and handle it generically in Parser.js? I am open to making substantial changes to the declarations and code for a cleaner overall solution.

Parser.ts

export type GrammarRules<Sym> = Record<Sym, GrammarRule<Sym>>
export type GrammarRule<Sym> = { lhs: Sym /* further uses of Sym type */ }

export class Parser<Sym> {
  rules: GrammarRules<Sym>
  private str: string
  /* Various other datastructure declarations with types dependent on Sym */

  constructor(rules: GrammarRules<Sym>) {
    this.rules = rules
  }
  parse(startSym: Sym, str: string) {
    this.str = str
    console.log(this.rules[startSym].lhs)
    // ...
  }
}

ParserLangABC.ts

import { Parser, GrammarRules } from "./Parser"

type Sym = 'A' | 'B' | 'C'
const rulesABC: GrammarRules<Sym> = {
  A: { lhs: 'A' /* rhs with further data of type Sym */ },
  B: { lhs: 'B' /* rhs with further data of type Sym */ },
  C: { lhs: 'C' /* rhs with further data of type Sym */ }
}
class ParserLangABC<Sym> extends Parser<Sym> {
  static parse(str: string) {
    const parser = new Parser(rulesABC)
    parser.parse('A', str)
  }
  // Other supporting methods parameterized to Sym
}

ParserLangDEF.ts

import { Parser, GrammarRules } from "./Parser"

type Sym = 'D' | 'E' | 'F'
const rulesDEF: GrammarRules<Sym> = {
  D: { lhs: 'D' /* rhs with further data of type Sym */ },
  E: { lhs: 'E' /* rhs with further data of type Sym */ },
  F: { lhs: 'F' /* rhs with further data of type Sym */ }
}
class ParserLangDEF<Sym> extends Parser<Sym> {
  static parse(str: string) {
    const parser = new Parser(rulesDEF)
    parser.parse('D', str)
  }
  // Other supporting methods parameterized to Sym
}

Answer №1

If you are working with parsers, it is essential to understand how templates play a crucial role in your process. One key aspect is defining

export type SymBase = string | number | symbol;
and replacing instances of Sym with Sym extends SymBase wherever necessary.

diff b/Parser.ts a/Parser.ts
1c1
< export type GrammarRules<Sym> = Record<Sym, GrammarRule<Sym>>
---
> export type SymBase = string | number | symbol;
> export type GrammarRules<Sym extends SymBase> = Record<Sym, GrammarRule<Sym>>
4c4
< export class Parser<Sym> {
---
> export class Parser<Sym extends SymBase> {
diff b/ParserLangABC.ts a/ParserLangABC.ts
1c1
< import { Parser, GrammarRules } from "./Parser"
---
> import { SymBase, Parser, GrammarRules } from "./Parser"
9c9
< class ParserLangABC<Sym> extends Parser<Sym> {
---
> class ParserLangABC<Sym extends SymBase> extends Parser<Sym> {
diff b/ParserLangDEF.ts a/ParserLangDEF.ts
1c1
< import { Parser, GrammarRules } from "./Parser"
---
> import { SymBase, Parser, GrammarRules } from "./Parser"
9c9
< class ParserLangABC<Sym> extends Parser<Sym> {
---
> class ParserLangABC<Sym extends SymBase> extends Parser<Sym> {

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Handling the onChange event in ReactJS using Typescript and Redux for number values with comma separators

Consider the following scenario: I am working on a reactjs/redux/typescript application. I have implemented two-way binding with redux for my input textboxes using the onChange event. The props are all declared with the data type "number" and everything i ...

Testing React Components - The `useClient` function should only be used within the `WagmiConfig` component

In my Next.js app with TypeScript, Jest, and React testing library, I encountered an error while trying to test a component. The error message states that `useClient` must be used within `WagmiConfig`. This issue arises because the `useAccount` hook relies ...

Tips for importing a module such as 'MyPersonalLibrary/data'

Currently, I am developing a project with two packages using Typescript and React-Native: The first package, PackageA (which is considered the leaf package), includes a REST client and mocks: MyOwnLibrary - src - tests - mocks - restClientMoc ...

Despite setting the esModuleInterop flag, I am still encountering an error with default imports

My React project with TypeScript is causing some issues. In the main tsx file, the import React from 'react' line works fine. However, in my test file, I keep getting the TS1259 error. I suspect there might be a problem with my TS/Jest/Babel conf ...

Combining Layouts and Providers: A Guide to Using Both on a Single Page

I am facing an issue with my provider that is responsible for refreshing a token by making a request to the server. Additionally, I have a basic layout featuring a sidebar that I want to use only on a specific route. However, I am unsure about where to add ...

Having difficulty navigating the features of the rxjs `merge` interface

Having trouble with the RxJs merge interface: export function merge<A extends readonly unknown[]>(...sources: [...ObservableInputTuple<A>]): Observable<A[number]>; So, based on this information, I developed the following code const alpha ...

Experiencing extended loading periods in Ionic 2 app

Struggling with slow load times on my Ionic 2 application. Even though I am using minimal plugins, the start up time is taking minutes and a white screen appears after the splash screen disappears. ...

NextJS 13 causes tailwind to malfunction when route group is utilized

I've encountered an issue in my NextJS 13 application where Tailwind classes are no longer being applied after moving page.tsx/layout.tsx from the root directory to a (main) directory within the root. I suspect that there may be a configuration that i ...

What is the correct way to implement the Maybe functor in typing?

I've been working on implementing a Maybe functor, inspired by Dr. Frisby's approach. Here is my code: interface Maybe<T> { isNothing: () => boolean; map: <S>(fn: (x: T) => S) => T extends null | undefined ? Maybe<S& ...

Error: Parsing error - Unrecognized character "@" in Angular module

Currently I am delving into the realm of webpack and attempting to integrate it into my project. However, I seem to have hit a roadblock as I encounter the following error. Despite my efforts to troubleshoot and research, I cannot seem to find a loader spe ...

Reorganize the data in a different manner (collections of items instead of individual items linked to groups)

Is there a way to create a function that converts data from `DevicesType` to `GroupsType` below? Instead of having a list of devices showing the groups they belong to, I need a list of groups with their respective devices. type DevicesType = { id: string ...

I am encountering an issue trying to create a Docker image featuring TypeScript

I am facing an issue while trying to build a docker image using the docker build . command in my next.js app An error is being encountered Error: buildx failed with: error: failed to solve: process "/bin/sh -c yarn run build" did not complete su ...

Typescript offers a feature where we can return the proper type from a generic function that is constrained by a lookup type,

Imagine we have the following function implementation: type Action = 'GREET' |'ASK' function getUnion<T extends Action>(action: T) { switch (action) { case 'GREET': return {hello: &a ...

Tips for instructing kysely key-gen to utilize basic data types for mapping database tables

While using the kysely-codegen tool to automatically create all models from my database, I have noticed a peculiar behavior. It seems to be adding a Generated type to every field instead of generating only number or boolean. Any idea why this is happening? ...

Flashing Screens with Ionic 2

I am currently dealing with a situation where my login page and homepage are involved. I have implemented native storage to set an item that helps in checking if the user is already logged in (either through Facebook or Google authentication). The logic fo ...

In TypeScript, a numerical value can be returned as a string when

After creating a numericQuantity variable that is a casted string quantity, I was puzzled as to why the typeof returns string even though the variable is supposed to be of type :number. let quantity: string = '10'; let numericQuantity: number = q ...

Tally up identical words without considering differences in capitalization or extra spaces

Let's take an example with different variations of the word "themselves" like "themselves", "Themselves", or " THEMSelveS " (notice the leading and trailing spaces), all should be considered as one count for themselves: 3 ...

What could be causing my ISO datetime string with timezone to alter the time during POST request?

When using the fetch API to POST a JSON object as a string to a service method, I encountered an issue with timezones being converted. Initially, the object contained ISO 8601 strings with timezones (e.g. "StartDate": "2019-04-16T13:46:04-06:00"), but upon ...

Effortlessly adding custom classes in Angular 2 with a breeze

Imagine having a growing Angular2 typescript solution with numerous small classes and objects. Following the best practice, each class is placed in its own file. However, manipulating these objects leads to long lists of imports like: import {Object1} f ...

Error in React-Typescript: The element type 'Component' is missing any construction or call signatures

I recently wrote a higher order component using React withContext: import React from 'react'; import permissionContext from 'context'; interface Props { Component: () => React.Component; } const withContext: React.FC<Props> ...