ohmjs / ohm

A library and language for building parsers, interpreters, compilers, etc.
MIT License
5.01k stars 217 forks source link

[TS] Fixing `keyof` type operator for `ActionDict` #395

Closed Phamier closed 2 years ago

Phamier commented 2 years ago

Introduction

Hi! πŸ‘‹

Firstly, thanks for your work on this project! πŸ™‚

Today I used patch-package to patch ohm-js@16.4.0 for the project I'm working on.

Problem

String mapping in type ActionDict breaks keyof type operator.

Example

I wanted to make a type containing actions keys of my grammar.

type MapKeys = Exclude<keyof FormulaActionDict<any>, keyof ActionDict<any>>;

Instead of returning union of string literal types keyof ActionDict<any> returns string | number. So type MapKeys == never.

Solution

Here is the diff that solved my problem:

diff --git a/node_modules/ohm-js/index.d.ts b/node_modules/ohm-js/index.d.ts
index 9265d48..db49216 100644
--- a/node_modules/ohm-js/index.d.ts
+++ b/node_modules/ohm-js/index.d.ts
@@ -273,8 +273,6 @@ declare namespace ohm {
    * An ActionDict is a dictionary of Actions indexed by rule names.
    */
   interface ActionDict<T> {
-    [index: string]: Action<T> | undefined;
-
     _iter?: (this: IterationNode, ...children: Node[]) => T;
     _nonterminal?: (this: NonterminalNode, ...children: Node[]) => T;
     _terminal?: (this: TerminalNode) => T;

This issue body was partially generated by patch-package.

pdubroy commented 2 years ago

Thanks for opening this issue. I think this makes sense now that we supported generating TypeScript type definitions for grammars. The original type definition was intended to give some basic type information for generic action dictionaries. This would be a breaking change though.

pdubroy commented 2 years ago

This is now fixed on main, and will be included in the upcoming v17 release. However, you'll have to use BaseActionDict to get the behaviour you're looking for.

import * as ohm from 'ohm-js';
import greeting, {GreetingActionDict} from './greeting.ohm-bundle';

type MapKey = Exclude<keyof GreetingActionDict<any>, keyof ohm.BaseActionDict<any>>;
const x: MapKey = 'hello';