Преглед изворни кода

feat: add generic types for parser and renderer output (#3722)

* fix: add generic types for parser and renderer output

* fix types

* rename generic variables

* add tests
Tony Brix пре 1 месец
родитељ
комит
39a0ee326d
11 измењених фајлова са 183 додато и 169 уклоњено
  1. 5 5
      src/Hooks.ts
  2. 27 27
      src/Instance.ts
  3. 9 9
      src/Lexer.ts
  4. 24 24
      src/MarkedOptions.ts
  5. 19 19
      src/Parser.ts
  6. 54 54
      src/Renderer.ts
  7. 19 19
      src/TextRenderer.ts
  8. 4 4
      src/Tokenizer.ts
  9. 2 2
      src/Tokens.ts
  10. 4 4
      src/defaults.ts
  11. 16 2
      test/types/marked.ts

+ 5 - 5
src/Hooks.ts

@@ -4,11 +4,11 @@ import { _Parser } from './Parser.ts';
 import type { MarkedOptions } from './MarkedOptions.ts';
 import type { MarkedOptions } from './MarkedOptions.ts';
 import type { Token, TokensList } from './Tokens.ts';
 import type { Token, TokensList } from './Tokens.ts';
 
 
-export class _Hooks {
-  options: MarkedOptions;
+export class _Hooks<ParserOutput = string, RendererOutput = string> {
+  options: MarkedOptions<ParserOutput, RendererOutput>;
   block?: boolean;
   block?: boolean;
 
 
-  constructor(options?: MarkedOptions) {
+  constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
     this.options = options || _defaults;
     this.options = options || _defaults;
   }
   }
 
 
@@ -28,7 +28,7 @@ export class _Hooks {
   /**
   /**
    * Process HTML after marked is finished
    * Process HTML after marked is finished
    */
    */
-  postprocess(html: string) {
+  postprocess(html: ParserOutput) {
     return html;
     return html;
   }
   }
 
 
@@ -50,6 +50,6 @@ export class _Hooks {
    * Provide function to parse tokens
    * Provide function to parse tokens
    */
    */
   provideParser() {
   provideParser() {
-    return this.block ? _Parser.parse : _Parser.parseInline;
+    return this.block ? _Parser.parse<ParserOutput, RendererOutput> : _Parser.parseInline<ParserOutput, RendererOutput>;
   }
   }
 }
 }

+ 27 - 27
src/Instance.ts

@@ -14,21 +14,21 @@ export type MaybePromise = void | Promise<void>;
 type UnknownFunction = (...args: unknown[]) => unknown;
 type UnknownFunction = (...args: unknown[]) => unknown;
 type GenericRendererFunction = (...args: unknown[]) => string | false;
 type GenericRendererFunction = (...args: unknown[]) => string | false;
 
 
-export class Marked {
-  defaults = _getDefaults();
+export class Marked<ParserOutput = string, RendererOutput = string> {
+  defaults = _getDefaults<ParserOutput, RendererOutput>();
   options = this.setOptions;
   options = this.setOptions;
 
 
   parse = this.parseMarkdown(true);
   parse = this.parseMarkdown(true);
   parseInline = this.parseMarkdown(false);
   parseInline = this.parseMarkdown(false);
 
 
-  Parser = _Parser;
-  Renderer = _Renderer;
-  TextRenderer = _TextRenderer;
+  Parser = _Parser<ParserOutput, RendererOutput>;
+  Renderer = _Renderer<ParserOutput, RendererOutput>;
+  TextRenderer = _TextRenderer<RendererOutput>;
   Lexer = _Lexer;
   Lexer = _Lexer;
-  Tokenizer = _Tokenizer;
-  Hooks = _Hooks;
+  Tokenizer = _Tokenizer<ParserOutput, RendererOutput>;
+  Hooks = _Hooks<ParserOutput, RendererOutput>;
 
 
-  constructor(...args: MarkedExtension[]) {
+  constructor(...args: MarkedExtension<ParserOutput, RendererOutput>[]) {
     this.use(...args);
     this.use(...args);
   }
   }
 
 
@@ -73,12 +73,12 @@ export class Marked {
     return values;
     return values;
   }
   }
 
 
-  use(...args: MarkedExtension[]) {
-    const extensions: MarkedOptions['extensions'] = this.defaults.extensions || { renderers: {}, childTokens: {} };
+  use(...args: MarkedExtension<ParserOutput, RendererOutput>[]) {
+    const extensions: MarkedOptions<ParserOutput, RendererOutput>['extensions'] = this.defaults.extensions || { renderers: {}, childTokens: {} };
 
 
     args.forEach((pack) => {
     args.forEach((pack) => {
       // copy options to new object
       // copy options to new object
-      const opts = { ...pack } as MarkedOptions;
+      const opts = { ...pack } as MarkedOptions<ParserOutput, RendererOutput>;
 
 
       // set async to true if it was set to true before
       // set async to true if it was set to true before
       opts.async = this.defaults.async || opts.async || false;
       opts.async = this.defaults.async || opts.async || false;
@@ -139,7 +139,7 @@ export class Marked {
 
 
       // ==-- Parse "overwrite" extensions --== //
       // ==-- Parse "overwrite" extensions --== //
       if (pack.renderer) {
       if (pack.renderer) {
-        const renderer = this.defaults.renderer || new _Renderer(this.defaults);
+        const renderer = this.defaults.renderer || new _Renderer<ParserOutput, RendererOutput>(this.defaults);
         for (const prop in pack.renderer) {
         for (const prop in pack.renderer) {
           if (!(prop in renderer)) {
           if (!(prop in renderer)) {
             throw new Error(`renderer '${prop}' does not exist`);
             throw new Error(`renderer '${prop}' does not exist`);
@@ -148,7 +148,7 @@ export class Marked {
             // ignore options property
             // ignore options property
             continue;
             continue;
           }
           }
-          const rendererProp = prop as Exclude<keyof _Renderer, 'options' | 'parser'>;
+          const rendererProp = prop as Exclude<keyof _Renderer<ParserOutput, RendererOutput>, 'options' | 'parser'>;
           const rendererFunc = pack.renderer[rendererProp] as GenericRendererFunction;
           const rendererFunc = pack.renderer[rendererProp] as GenericRendererFunction;
           const prevRenderer = renderer[rendererProp] as GenericRendererFunction;
           const prevRenderer = renderer[rendererProp] as GenericRendererFunction;
           // Replace renderer with func to run extension, but fall back if false
           // Replace renderer with func to run extension, but fall back if false
@@ -157,13 +157,13 @@ export class Marked {
             if (ret === false) {
             if (ret === false) {
               ret = prevRenderer.apply(renderer, args);
               ret = prevRenderer.apply(renderer, args);
             }
             }
-            return ret || '';
+            return (ret || '') as RendererOutput;
           };
           };
         }
         }
         opts.renderer = renderer;
         opts.renderer = renderer;
       }
       }
       if (pack.tokenizer) {
       if (pack.tokenizer) {
-        const tokenizer = this.defaults.tokenizer || new _Tokenizer(this.defaults);
+        const tokenizer = this.defaults.tokenizer || new _Tokenizer<ParserOutput, RendererOutput>(this.defaults);
         for (const prop in pack.tokenizer) {
         for (const prop in pack.tokenizer) {
           if (!(prop in tokenizer)) {
           if (!(prop in tokenizer)) {
             throw new Error(`tokenizer '${prop}' does not exist`);
             throw new Error(`tokenizer '${prop}' does not exist`);
@@ -172,7 +172,7 @@ export class Marked {
             // ignore options, rules, and lexer properties
             // ignore options, rules, and lexer properties
             continue;
             continue;
           }
           }
-          const tokenizerProp = prop as Exclude<keyof _Tokenizer, 'options' | 'rules' | 'lexer'>;
+          const tokenizerProp = prop as Exclude<keyof _Tokenizer<ParserOutput, RendererOutput>, 'options' | 'rules' | 'lexer'>;
           const tokenizerFunc = pack.tokenizer[tokenizerProp] as UnknownFunction;
           const tokenizerFunc = pack.tokenizer[tokenizerProp] as UnknownFunction;
           const prevTokenizer = tokenizer[tokenizerProp] as UnknownFunction;
           const prevTokenizer = tokenizer[tokenizerProp] as UnknownFunction;
           // Replace tokenizer with func to run extension, but fall back if false
           // Replace tokenizer with func to run extension, but fall back if false
@@ -190,7 +190,7 @@ export class Marked {
 
 
       // ==-- Parse Hooks extensions --== //
       // ==-- Parse Hooks extensions --== //
       if (pack.hooks) {
       if (pack.hooks) {
-        const hooks = this.defaults.hooks || new _Hooks();
+        const hooks = this.defaults.hooks || new _Hooks<ParserOutput, RendererOutput>();
         for (const prop in pack.hooks) {
         for (const prop in pack.hooks) {
           if (!(prop in hooks)) {
           if (!(prop in hooks)) {
             throw new Error(`hook '${prop}' does not exist`);
             throw new Error(`hook '${prop}' does not exist`);
@@ -199,7 +199,7 @@ export class Marked {
             // ignore options and block properties
             // ignore options and block properties
             continue;
             continue;
           }
           }
-          const hooksProp = prop as Exclude<keyof _Hooks, 'options' | 'block'>;
+          const hooksProp = prop as Exclude<keyof _Hooks<ParserOutput, RendererOutput>, 'options' | 'block'>;
           const hooksFunc = pack.hooks[hooksProp] as UnknownFunction;
           const hooksFunc = pack.hooks[hooksProp] as UnknownFunction;
           const prevHook = hooks[hooksProp] as UnknownFunction;
           const prevHook = hooks[hooksProp] as UnknownFunction;
           if (_Hooks.passThroughHooks.has(prop)) {
           if (_Hooks.passThroughHooks.has(prop)) {
@@ -248,28 +248,28 @@ export class Marked {
     return this;
     return this;
   }
   }
 
 
-  setOptions(opt: MarkedOptions) {
+  setOptions(opt: MarkedOptions<ParserOutput, RendererOutput>) {
     this.defaults = { ...this.defaults, ...opt };
     this.defaults = { ...this.defaults, ...opt };
     return this;
     return this;
   }
   }
 
 
-  lexer(src: string, options?: MarkedOptions) {
+  lexer(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>) {
     return _Lexer.lex(src, options ?? this.defaults);
     return _Lexer.lex(src, options ?? this.defaults);
   }
   }
 
 
-  parser(tokens: Token[], options?: MarkedOptions) {
-    return _Parser.parse(tokens, options ?? this.defaults);
+  parser(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>) {
+    return _Parser.parse<ParserOutput, RendererOutput>(tokens, options ?? this.defaults);
   }
   }
 
 
   private parseMarkdown(blockType: boolean) {
   private parseMarkdown(blockType: boolean) {
     type overloadedParse = {
     type overloadedParse = {
-      (src: string, options: MarkedOptions & { async: true }): Promise<string>;
-      (src: string, options: MarkedOptions & { async: false }): string;
-      (src: string, options?: MarkedOptions | null): string | Promise<string>;
+      (src: string, options: MarkedOptions<ParserOutput, RendererOutput> & { async: true }): Promise<ParserOutput>;
+      (src: string, options: MarkedOptions<ParserOutput, RendererOutput> & { async: false }): ParserOutput;
+      (src: string, options?: MarkedOptions<ParserOutput, RendererOutput> | null): ParserOutput | Promise<ParserOutput>;
     };
     };
 
 
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    const parse: overloadedParse = (src: string, options?: MarkedOptions | null): any => {
+    const parse: overloadedParse = (src: string, options?: MarkedOptions<ParserOutput, RendererOutput> | null): any => {
       const origOpt = { ...options };
       const origOpt = { ...options };
       const opt = { ...this.defaults, ...origOpt };
       const opt = { ...this.defaults, ...origOpt };
 
 
@@ -320,7 +320,7 @@ export class Marked {
         }
         }
         let html = parser(tokens, opt);
         let html = parser(tokens, opt);
         if (opt.hooks) {
         if (opt.hooks) {
-          html = opt.hooks.postprocess(html) as string;
+          html = opt.hooks.postprocess(html);
         }
         }
         return html;
         return html;
       } catch(e) {
       } catch(e) {

+ 9 - 9
src/Lexer.ts

@@ -7,24 +7,24 @@ import type { MarkedOptions } from './MarkedOptions.ts';
 /**
 /**
  * Block Lexer
  * Block Lexer
  */
  */
-export class _Lexer {
+export class _Lexer<ParserOutput = string, RendererOutput = string> {
   tokens: TokensList;
   tokens: TokensList;
-  options: MarkedOptions;
+  options: MarkedOptions<ParserOutput, RendererOutput>;
   state: {
   state: {
     inLink: boolean;
     inLink: boolean;
     inRawBlock: boolean;
     inRawBlock: boolean;
     top: boolean;
     top: boolean;
   };
   };
 
 
-  private tokenizer: _Tokenizer;
+  private tokenizer: _Tokenizer<ParserOutput, RendererOutput>;
   private inlineQueue: { src: string, tokens: Token[] }[];
   private inlineQueue: { src: string, tokens: Token[] }[];
 
 
-  constructor(options?: MarkedOptions) {
+  constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
     // TokenList cannot be created in one go
     // TokenList cannot be created in one go
     this.tokens = [] as unknown as TokensList;
     this.tokens = [] as unknown as TokensList;
     this.tokens.links = Object.create(null);
     this.tokens.links = Object.create(null);
     this.options = options || _defaults;
     this.options = options || _defaults;
-    this.options.tokenizer = this.options.tokenizer || new _Tokenizer();
+    this.options.tokenizer = this.options.tokenizer || new _Tokenizer<ParserOutput, RendererOutput>();
     this.tokenizer = this.options.tokenizer;
     this.tokenizer = this.options.tokenizer;
     this.tokenizer.options = this.options;
     this.tokenizer.options = this.options;
     this.tokenizer.lexer = this;
     this.tokenizer.lexer = this;
@@ -68,16 +68,16 @@ export class _Lexer {
   /**
   /**
    * Static Lex Method
    * Static Lex Method
    */
    */
-  static lex(src: string, options?: MarkedOptions) {
-    const lexer = new _Lexer(options);
+  static lex<ParserOutput = string, RendererOutput = string>(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>) {
+    const lexer = new _Lexer<ParserOutput, RendererOutput>(options);
     return lexer.lex(src);
     return lexer.lex(src);
   }
   }
 
 
   /**
   /**
    * Static Lex Inline Method
    * Static Lex Inline Method
    */
    */
-  static lexInline(src: string, options?: MarkedOptions) {
-    const lexer = new _Lexer(options);
+  static lexInline<ParserOutput = string, RendererOutput = string>(src: string, options?: MarkedOptions<ParserOutput, RendererOutput>) {
+    const lexer = new _Lexer<ParserOutput, RendererOutput>(options);
     return lexer.inlineTokens(src);
     return lexer.inlineTokens(src);
   }
   }
 
 

+ 24 - 24
src/MarkedOptions.ts

@@ -21,35 +21,35 @@ export interface TokenizerExtension {
   childTokens?: string[];
   childTokens?: string[];
 }
 }
 
 
-export interface RendererThis {
-  parser: _Parser;
+export interface RendererThis<ParserOutput = string, RendererOutput = string> {
+  parser: _Parser<ParserOutput, RendererOutput>;
 }
 }
 
 
-export type RendererExtensionFunction = (this: RendererThis, token: Tokens.Generic) => string | false | undefined;
+export type RendererExtensionFunction<ParserOutput = string, RendererOutput = string> = (this: RendererThis<ParserOutput, RendererOutput>, token: Tokens.Generic) => RendererOutput | false | undefined;
 
 
-export interface RendererExtension {
+export interface RendererExtension<ParserOutput = string, RendererOutput = string> {
   name: string;
   name: string;
-  renderer: RendererExtensionFunction;
+  renderer: RendererExtensionFunction<ParserOutput, RendererOutput>;
 }
 }
 
 
-export type TokenizerAndRendererExtension = TokenizerExtension | RendererExtension | (TokenizerExtension & RendererExtension);
+export type TokenizerAndRendererExtension<ParserOutput = string, RendererOutput = string> = TokenizerExtension | RendererExtension<ParserOutput, RendererOutput> | (TokenizerExtension & RendererExtension<ParserOutput, RendererOutput>);
 
 
-type HooksApi = Omit<_Hooks, 'constructor' | 'options' | 'block'>;
-type HooksObject = {
-  [K in keyof HooksApi]?: (this: _Hooks, ...args: Parameters<HooksApi[K]>) => ReturnType<HooksApi[K]> | Promise<ReturnType<HooksApi[K]>>
+type HooksApi<ParserOutput = string, RendererOutput = string> = Omit<_Hooks<ParserOutput, RendererOutput>, 'constructor' | 'options' | 'block'>;
+type HooksObject<ParserOutput = string, RendererOutput = string> = {
+  [K in keyof HooksApi<ParserOutput, RendererOutput>]?: (this: _Hooks<ParserOutput, RendererOutput>, ...args: Parameters<HooksApi<ParserOutput, RendererOutput>[K]>) => ReturnType<HooksApi<ParserOutput, RendererOutput>[K]> | Promise<ReturnType<HooksApi<ParserOutput, RendererOutput>[K]>>
 };
 };
 
 
-type RendererApi = Omit<_Renderer, 'constructor' | 'options' | 'parser'>;
-type RendererObject = {
-  [K in keyof RendererApi]?: (this: _Renderer, ...args: Parameters<RendererApi[K]>) => ReturnType<RendererApi[K]> | false
+type RendererApi<ParserOutput = string, RendererOutput = string> = Omit<_Renderer<ParserOutput, RendererOutput>, 'constructor' | 'options' | 'parser'>;
+type RendererObject<ParserOutput = string, RendererOutput = string> = {
+  [K in keyof RendererApi<ParserOutput, RendererOutput>]?: (this: _Renderer<ParserOutput, RendererOutput>, ...args: Parameters<RendererApi<ParserOutput, RendererOutput>[K]>) => ReturnType<RendererApi<ParserOutput, RendererOutput>[K]> | false
 };
 };
 
 
-type TokenizerApi = Omit<_Tokenizer, 'constructor' | 'options' | 'rules' | 'lexer'>;
-type TokenizerObject = {
-  [K in keyof TokenizerApi]?: (this: _Tokenizer, ...args: Parameters<TokenizerApi[K]>) => ReturnType<TokenizerApi[K]> | false
+type TokenizerApi<ParserOutput = string, RendererOutput = string> = Omit<_Tokenizer<ParserOutput, RendererOutput>, 'constructor' | 'options' | 'rules' | 'lexer'>;
+type TokenizerObject<ParserOutput = string, RendererOutput = string> = {
+  [K in keyof TokenizerApi<ParserOutput, RendererOutput>]?: (this: _Tokenizer<ParserOutput, RendererOutput>, ...args: Parameters<TokenizerApi<ParserOutput, RendererOutput>[K]>) => ReturnType<TokenizerApi<ParserOutput, RendererOutput>[K]> | false
 };
 };
 
 
-export interface MarkedExtension {
+export interface MarkedExtension<ParserOutput = string, RendererOutput = string> {
   /**
   /**
    * True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string.
    * True will tell marked to await any walkTokens functions before parsing the tokens and returning an HTML string.
    */
    */
@@ -64,7 +64,7 @@ export interface MarkedExtension {
    * Add tokenizers and renderers to marked
    * Add tokenizers and renderers to marked
    */
    */
   extensions?:
   extensions?:
-    | TokenizerAndRendererExtension[]
+    | TokenizerAndRendererExtension<ParserOutput, RendererOutput>[]
     | null;
     | null;
 
 
   /**
   /**
@@ -80,7 +80,7 @@ export interface MarkedExtension {
    * provideLexer is called to provide a function to tokenize markdown.
    * provideLexer is called to provide a function to tokenize markdown.
    * provideParser is called to provide a function to parse tokens.
    * provideParser is called to provide a function to parse tokens.
    */
    */
-  hooks?: HooksObject | null;
+  hooks?: HooksObject<ParserOutput, RendererOutput> | null;
 
 
   /**
   /**
    * Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
    * Conform to obscure parts of markdown.pl as much as possible. Don't fix any of the original markdown bugs or poor behavior.
@@ -92,7 +92,7 @@ export interface MarkedExtension {
    *
    *
    * An object containing functions to render tokens to HTML.
    * An object containing functions to render tokens to HTML.
    */
    */
-  renderer?: RendererObject | null;
+  renderer?: RendererObject<ParserOutput, RendererOutput> | null;
 
 
   /**
   /**
    * Shows an HTML error message when rendering fails.
    * Shows an HTML error message when rendering fails.
@@ -113,30 +113,30 @@ export interface MarkedExtension {
   walkTokens?: ((token: Token) => void | Promise<void>) | null;
   walkTokens?: ((token: Token) => void | Promise<void>) | null;
 }
 }
 
 
-export interface MarkedOptions extends Omit<MarkedExtension, 'hooks' | 'renderer' | 'tokenizer' | 'extensions' | 'walkTokens'> {
+export interface MarkedOptions<ParserOutput = string, RendererOutput = string> extends Omit<MarkedExtension<ParserOutput, RendererOutput>, 'hooks' | 'renderer' | 'tokenizer' | 'extensions' | 'walkTokens'> {
   /**
   /**
    * Hooks are methods that hook into some part of marked.
    * Hooks are methods that hook into some part of marked.
    */
    */
-  hooks?: _Hooks | null;
+  hooks?: _Hooks<ParserOutput, RendererOutput> | null;
 
 
   /**
   /**
    * Type: object Default: new Renderer()
    * Type: object Default: new Renderer()
    *
    *
    * An object containing functions to render tokens to HTML.
    * An object containing functions to render tokens to HTML.
    */
    */
-  renderer?: _Renderer | null;
+  renderer?: _Renderer<ParserOutput, RendererOutput> | null;
 
 
   /**
   /**
    * The tokenizer defines how to turn markdown text into tokens.
    * The tokenizer defines how to turn markdown text into tokens.
    */
    */
-  tokenizer?: _Tokenizer | null;
+  tokenizer?: _Tokenizer<ParserOutput, RendererOutput> | null;
 
 
   /**
   /**
    * Custom extensions
    * Custom extensions
    */
    */
   extensions?: null | {
   extensions?: null | {
     renderers: {
     renderers: {
-      [name: string]: RendererExtensionFunction;
+      [name: string]: RendererExtensionFunction<ParserOutput, RendererOutput>;
     };
     };
     childTokens: {
     childTokens: {
       [name: string]: string[];
       [name: string]: string[];

+ 19 - 19
src/Parser.ts

@@ -7,39 +7,39 @@ import type { MarkedOptions } from './MarkedOptions.ts';
 /**
 /**
  * Parsing & Compiling
  * Parsing & Compiling
  */
  */
-export class _Parser {
-  options: MarkedOptions;
-  renderer: _Renderer;
-  textRenderer: _TextRenderer;
-  constructor(options?: MarkedOptions) {
+export class _Parser<ParserOutput = string, RendererOutput = string> {
+  options: MarkedOptions<ParserOutput, RendererOutput>;
+  renderer: _Renderer<ParserOutput, RendererOutput>;
+  textRenderer: _TextRenderer<RendererOutput>;
+  constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
     this.options = options || _defaults;
     this.options = options || _defaults;
-    this.options.renderer = this.options.renderer || new _Renderer();
+    this.options.renderer = this.options.renderer || new _Renderer<ParserOutput, RendererOutput>();
     this.renderer = this.options.renderer;
     this.renderer = this.options.renderer;
     this.renderer.options = this.options;
     this.renderer.options = this.options;
     this.renderer.parser = this;
     this.renderer.parser = this;
-    this.textRenderer = new _TextRenderer();
+    this.textRenderer = new _TextRenderer<RendererOutput>();
   }
   }
 
 
   /**
   /**
    * Static Parse Method
    * Static Parse Method
    */
    */
-  static parse(tokens: Token[], options?: MarkedOptions) {
-    const parser = new _Parser(options);
+  static parse<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>) {
+    const parser = new _Parser<ParserOutput, RendererOutput>(options);
     return parser.parse(tokens);
     return parser.parse(tokens);
   }
   }
 
 
   /**
   /**
    * Static Parse Inline Method
    * Static Parse Inline Method
    */
    */
-  static parseInline(tokens: Token[], options?: MarkedOptions) {
-    const parser = new _Parser(options);
+  static parseInline<ParserOutput = string, RendererOutput = string>(tokens: Token[], options?: MarkedOptions<ParserOutput, RendererOutput>) {
+    const parser = new _Parser<ParserOutput, RendererOutput>(options);
     return parser.parseInline(tokens);
     return parser.parseInline(tokens);
   }
   }
 
 
   /**
   /**
    * Parse Loop
    * Parse Loop
    */
    */
-  parse(tokens: Token[], top = true): string {
+  parse(tokens: Token[], top = true): ParserOutput {
     let out = '';
     let out = '';
 
 
     for (let i = 0; i < tokens.length; i++) {
     for (let i = 0; i < tokens.length; i++) {
@@ -96,10 +96,10 @@ export class _Parser {
         }
         }
         case 'text': {
         case 'text': {
           let textToken = token;
           let textToken = token;
-          let body = this.renderer.text(textToken);
+          let body = this.renderer.text(textToken) as string;
           while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
           while (i + 1 < tokens.length && tokens[i + 1].type === 'text') {
             textToken = tokens[++i] as Tokens.Text;
             textToken = tokens[++i] as Tokens.Text;
-            body += '\n' + this.renderer.text(textToken);
+            body += ('\n' + this.renderer.text(textToken));
           }
           }
           if (top) {
           if (top) {
             out += this.renderer.paragraph({
             out += this.renderer.paragraph({
@@ -118,7 +118,7 @@ export class _Parser {
           const errMsg = 'Token with "' + token.type + '" type was not found.';
           const errMsg = 'Token with "' + token.type + '" type was not found.';
           if (this.options.silent) {
           if (this.options.silent) {
             console.error(errMsg);
             console.error(errMsg);
-            return '';
+            return '' as ParserOutput;
           } else {
           } else {
             throw new Error(errMsg);
             throw new Error(errMsg);
           }
           }
@@ -126,13 +126,13 @@ export class _Parser {
       }
       }
     }
     }
 
 
-    return out;
+    return out as ParserOutput;
   }
   }
 
 
   /**
   /**
    * Parse Inline Tokens
    * Parse Inline Tokens
    */
    */
-  parseInline(tokens: Token[], renderer: _Renderer | _TextRenderer = this.renderer): string {
+  parseInline(tokens: Token[], renderer: _Renderer<ParserOutput, RendererOutput> | _TextRenderer<RendererOutput> = this.renderer): ParserOutput {
     let out = '';
     let out = '';
 
 
     for (let i = 0; i < tokens.length; i++) {
     for (let i = 0; i < tokens.length; i++) {
@@ -194,13 +194,13 @@ export class _Parser {
           const errMsg = 'Token with "' + token.type + '" type was not found.';
           const errMsg = 'Token with "' + token.type + '" type was not found.';
           if (this.options.silent) {
           if (this.options.silent) {
             console.error(errMsg);
             console.error(errMsg);
-            return '';
+            return '' as ParserOutput;
           } else {
           } else {
             throw new Error(errMsg);
             throw new Error(errMsg);
           }
           }
         }
         }
       }
       }
     }
     }
-    return out;
+    return out as ParserOutput;
   }
   }
 }
 }

+ 54 - 54
src/Renderer.ts

@@ -11,18 +11,18 @@ import type { _Parser } from './Parser.ts';
 /**
 /**
  * Renderer
  * Renderer
  */
  */
-export class _Renderer {
-  options: MarkedOptions;
-  parser!: _Parser; // set by the parser
-  constructor(options?: MarkedOptions) {
+export class _Renderer<ParserOutput = string, RendererOutput = string> {
+  options: MarkedOptions<ParserOutput, RendererOutput>;
+  parser!: _Parser<ParserOutput, RendererOutput>; // set by the parser
+  constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
     this.options = options || _defaults;
     this.options = options || _defaults;
   }
   }
 
 
-  space(token: Tokens.Space): string {
-    return '';
+  space(token: Tokens.Space): RendererOutput {
+    return '' as RendererOutput;
   }
   }
 
 
-  code({ text, lang, escaped }: Tokens.Code): string {
+  code({ text, lang, escaped }: Tokens.Code): RendererOutput {
     const langString = (lang || '').match(other.notSpaceStart)?.[0];
     const langString = (lang || '').match(other.notSpaceStart)?.[0];
 
 
     const code = text.replace(other.endingNewline, '') + '\n';
     const code = text.replace(other.endingNewline, '') + '\n';
@@ -30,34 +30,34 @@ export class _Renderer {
     if (!langString) {
     if (!langString) {
       return '<pre><code>'
       return '<pre><code>'
         + (escaped ? code : escape(code, true))
         + (escaped ? code : escape(code, true))
-        + '</code></pre>\n';
+        + '</code></pre>\n' as RendererOutput;
     }
     }
 
 
     return '<pre><code class="language-'
     return '<pre><code class="language-'
       + escape(langString)
       + escape(langString)
       + '">'
       + '">'
       + (escaped ? code : escape(code, true))
       + (escaped ? code : escape(code, true))
-      + '</code></pre>\n';
+      + '</code></pre>\n' as RendererOutput;
   }
   }
 
 
-  blockquote({ tokens }: Tokens.Blockquote): string {
+  blockquote({ tokens }: Tokens.Blockquote): RendererOutput {
     const body = this.parser.parse(tokens);
     const body = this.parser.parse(tokens);
-    return `<blockquote>\n${body}</blockquote>\n`;
+    return `<blockquote>\n${body}</blockquote>\n` as RendererOutput;
   }
   }
 
 
-  html({ text }: Tokens.HTML | Tokens.Tag) : string {
-    return text;
+  html({ text }: Tokens.HTML | Tokens.Tag): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  heading({ tokens, depth }: Tokens.Heading): string {
-    return `<h${depth}>${this.parser.parseInline(tokens)}</h${depth}>\n`;
+  heading({ tokens, depth }: Tokens.Heading): RendererOutput {
+    return `<h${depth}>${this.parser.parseInline(tokens)}</h${depth}>\n` as RendererOutput;
   }
   }
 
 
-  hr(token: Tokens.Hr): string {
-    return '<hr>\n';
+  hr(token: Tokens.Hr): RendererOutput {
+    return '<hr>\n' as RendererOutput;
   }
   }
 
 
-  list(token: Tokens.List): string {
+  list(token: Tokens.List): RendererOutput {
     const ordered = token.ordered;
     const ordered = token.ordered;
     const start = token.start;
     const start = token.start;
 
 
@@ -69,10 +69,10 @@ export class _Renderer {
 
 
     const type = ordered ? 'ol' : 'ul';
     const type = ordered ? 'ol' : 'ul';
     const startAttr = (ordered && start !== 1) ? (' start="' + start + '"') : '';
     const startAttr = (ordered && start !== 1) ? (' start="' + start + '"') : '';
-    return '<' + type + startAttr + '>\n' + body + '</' + type + '>\n';
+    return '<' + type + startAttr + '>\n' + body + '</' + type + '>\n' as RendererOutput;
   }
   }
 
 
-  listitem(item: Tokens.ListItem): string {
+  listitem(item: Tokens.ListItem): RendererOutput {
     let itemBody = '';
     let itemBody = '';
     if (item.task) {
     if (item.task) {
       const checkbox = this.checkbox({ checked: !!item.checked });
       const checkbox = this.checkbox({ checked: !!item.checked });
@@ -98,20 +98,20 @@ export class _Renderer {
 
 
     itemBody += this.parser.parse(item.tokens, !!item.loose);
     itemBody += this.parser.parse(item.tokens, !!item.loose);
 
 
-    return `<li>${itemBody}</li>\n`;
+    return `<li>${itemBody}</li>\n` as RendererOutput;
   }
   }
 
 
-  checkbox({ checked }: Tokens.Checkbox): string {
+  checkbox({ checked }: Tokens.Checkbox): RendererOutput {
     return '<input '
     return '<input '
       + (checked ? 'checked="" ' : '')
       + (checked ? 'checked="" ' : '')
-      + 'disabled="" type="checkbox">';
+      + 'disabled="" type="checkbox">' as RendererOutput;
   }
   }
 
 
-  paragraph({ tokens }: Tokens.Paragraph): string {
-    return `<p>${this.parser.parseInline(tokens)}</p>\n`;
+  paragraph({ tokens }: Tokens.Paragraph): RendererOutput {
+    return `<p>${this.parser.parseInline(tokens)}</p>\n` as RendererOutput;
   }
   }
 
 
-  table(token: Tokens.Table): string {
+  table(token: Tokens.Table): RendererOutput {
     let header = '';
     let header = '';
 
 
     // header
     // header
@@ -119,7 +119,7 @@ export class _Renderer {
     for (let j = 0; j < token.header.length; j++) {
     for (let j = 0; j < token.header.length; j++) {
       cell += this.tablecell(token.header[j]);
       cell += this.tablecell(token.header[j]);
     }
     }
-    header += this.tablerow({ text: cell });
+    header += this.tablerow({ text: cell as ParserOutput });
 
 
     let body = '';
     let body = '';
     for (let j = 0; j < token.rows.length; j++) {
     for (let j = 0; j < token.rows.length; j++) {
@@ -130,7 +130,7 @@ export class _Renderer {
         cell += this.tablecell(row[k]);
         cell += this.tablecell(row[k]);
       }
       }
 
 
-      body += this.tablerow({ text: cell });
+      body += this.tablerow({ text: cell as ParserOutput });
     }
     }
     if (body) body = `<tbody>${body}</tbody>`;
     if (body) body = `<tbody>${body}</tbody>`;
 
 
@@ -139,50 +139,50 @@ export class _Renderer {
       + header
       + header
       + '</thead>\n'
       + '</thead>\n'
       + body
       + body
-      + '</table>\n';
+      + '</table>\n' as RendererOutput;
   }
   }
 
 
-  tablerow({ text }: Tokens.TableRow): string {
-    return `<tr>\n${text}</tr>\n`;
+  tablerow({ text }: Tokens.TableRow<ParserOutput>): RendererOutput {
+    return `<tr>\n${text}</tr>\n` as RendererOutput;
   }
   }
 
 
-  tablecell(token: Tokens.TableCell): string {
+  tablecell(token: Tokens.TableCell): RendererOutput {
     const content = this.parser.parseInline(token.tokens);
     const content = this.parser.parseInline(token.tokens);
     const type = token.header ? 'th' : 'td';
     const type = token.header ? 'th' : 'td';
     const tag = token.align
     const tag = token.align
       ? `<${type} align="${token.align}">`
       ? `<${type} align="${token.align}">`
       : `<${type}>`;
       : `<${type}>`;
-    return tag + content + `</${type}>\n`;
+    return tag + content + `</${type}>\n` as RendererOutput;
   }
   }
 
 
   /**
   /**
    * span level renderer
    * span level renderer
    */
    */
-  strong({ tokens }: Tokens.Strong): string {
-    return `<strong>${this.parser.parseInline(tokens)}</strong>`;
+  strong({ tokens }: Tokens.Strong): RendererOutput {
+    return `<strong>${this.parser.parseInline(tokens)}</strong>` as RendererOutput;
   }
   }
 
 
-  em({ tokens }: Tokens.Em): string {
-    return `<em>${this.parser.parseInline(tokens)}</em>`;
+  em({ tokens }: Tokens.Em): RendererOutput {
+    return `<em>${this.parser.parseInline(tokens)}</em>` as RendererOutput;
   }
   }
 
 
-  codespan({ text }: Tokens.Codespan): string {
-    return `<code>${escape(text, true)}</code>`;
+  codespan({ text }: Tokens.Codespan): RendererOutput {
+    return `<code>${escape(text, true)}</code>` as RendererOutput;
   }
   }
 
 
-  br(token: Tokens.Br): string {
-    return '<br>';
+  br(token: Tokens.Br): RendererOutput {
+    return '<br>' as RendererOutput;
   }
   }
 
 
-  del({ tokens }: Tokens.Del): string {
-    return `<del>${this.parser.parseInline(tokens)}</del>`;
+  del({ tokens }: Tokens.Del): RendererOutput {
+    return `<del>${this.parser.parseInline(tokens)}</del>` as RendererOutput;
   }
   }
 
 
-  link({ href, title, tokens }: Tokens.Link): string {
-    const text = this.parser.parseInline(tokens);
+  link({ href, title, tokens }: Tokens.Link): RendererOutput {
+    const text = this.parser.parseInline(tokens) as string;
     const cleanHref = cleanUrl(href);
     const cleanHref = cleanUrl(href);
     if (cleanHref === null) {
     if (cleanHref === null) {
-      return text;
+      return text as RendererOutput;
     }
     }
     href = cleanHref;
     href = cleanHref;
     let out = '<a href="' + href + '"';
     let out = '<a href="' + href + '"';
@@ -190,16 +190,16 @@ export class _Renderer {
       out += ' title="' + (escape(title)) + '"';
       out += ' title="' + (escape(title)) + '"';
     }
     }
     out += '>' + text + '</a>';
     out += '>' + text + '</a>';
-    return out;
+    return out as RendererOutput;
   }
   }
 
 
-  image({ href, title, text, tokens }: Tokens.Image): string {
+  image({ href, title, text, tokens }: Tokens.Image): RendererOutput {
     if (tokens) {
     if (tokens) {
-      text = this.parser.parseInline(tokens, this.parser.textRenderer);
+      text = this.parser.parseInline(tokens, this.parser.textRenderer) as string;
     }
     }
     const cleanHref = cleanUrl(href);
     const cleanHref = cleanUrl(href);
     if (cleanHref === null) {
     if (cleanHref === null) {
-      return escape(text);
+      return escape(text) as RendererOutput;
     }
     }
     href = cleanHref;
     href = cleanHref;
 
 
@@ -208,12 +208,12 @@ export class _Renderer {
       out += ` title="${escape(title)}"`;
       out += ` title="${escape(title)}"`;
     }
     }
     out += '>';
     out += '>';
-    return out;
+    return out as RendererOutput;
   }
   }
 
 
-  text(token: Tokens.Text | Tokens.Escape) : string {
+  text(token: Tokens.Text | Tokens.Escape): RendererOutput {
     return 'tokens' in token && token.tokens
     return 'tokens' in token && token.tokens
-      ? this.parser.parseInline(token.tokens)
-      : ('escaped' in token && token.escaped ? token.text : escape(token.text));
+      ? this.parser.parseInline(token.tokens) as unknown as RendererOutput
+      : ('escaped' in token && token.escaped ? token.text as RendererOutput : escape(token.text) as RendererOutput);
   }
   }
 }
 }

+ 19 - 19
src/TextRenderer.ts

@@ -4,41 +4,41 @@ import type { Tokens } from './Tokens.ts';
  * TextRenderer
  * TextRenderer
  * returns only the textual part of the token
  * returns only the textual part of the token
  */
  */
-export class _TextRenderer {
+export class _TextRenderer<RendererOutput = string> {
   // no need for block level renderers
   // no need for block level renderers
-  strong({ text }: Tokens.Strong) {
-    return text;
+  strong({ text }: Tokens.Strong): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  em({ text }: Tokens.Em) {
-    return text;
+  em({ text }: Tokens.Em): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  codespan({ text }: Tokens.Codespan) {
-    return text;
+  codespan({ text }: Tokens.Codespan): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  del({ text }: Tokens.Del) {
-    return text;
+  del({ text }: Tokens.Del): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  html({ text }: Tokens.HTML | Tokens.Tag) {
-    return text;
+  html({ text }: Tokens.HTML | Tokens.Tag): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  text({ text }: Tokens.Text | Tokens.Escape | Tokens.Tag) {
-    return text;
+  text({ text }: Tokens.Text | Tokens.Escape | Tokens.Tag): RendererOutput {
+    return text as RendererOutput;
   }
   }
 
 
-  link({ text }: Tokens.Link) {
-    return '' + text;
+  link({ text }: Tokens.Link): RendererOutput {
+    return '' + text as RendererOutput;
   }
   }
 
 
-  image({ text }: Tokens.Image) {
-    return '' + text;
+  image({ text }: Tokens.Image): RendererOutput {
+    return '' + text as RendererOutput;
   }
   }
 
 
-  br() {
-    return '';
+  br(): RendererOutput {
+    return '' as RendererOutput;
   }
   }
 }
 }

+ 4 - 4
src/Tokenizer.ts

@@ -58,12 +58,12 @@ function indentCodeCompensation(raw: string, text: string, rules: Rules) {
 /**
 /**
  * Tokenizer
  * Tokenizer
  */
  */
-export class _Tokenizer {
-  options: MarkedOptions;
+export class _Tokenizer<ParserOutput = string, RendererOutput = string> {
+  options: MarkedOptions<ParserOutput, RendererOutput>;
   rules!: Rules; // set by the lexer
   rules!: Rules; // set by the lexer
-  lexer!: _Lexer; // set by the lexer
+  lexer!: _Lexer<ParserOutput, RendererOutput>; // set by the lexer
 
 
-  constructor(options?: MarkedOptions) {
+  constructor(options?: MarkedOptions<ParserOutput, RendererOutput>) {
     this.options = options || _defaults;
     this.options = options || _defaults;
   }
   }
 
 

+ 2 - 2
src/Tokens.ts

@@ -189,8 +189,8 @@ export namespace Tokens {
     align: 'center' | 'left' | 'right' | null;
     align: 'center' | 'left' | 'right' | null;
   }
   }
 
 
-  export interface TableRow {
-    text: string;
+  export interface TableRow<P = string> {
+    text: P;
   }
   }
 
 
   export interface Tag {
   export interface Tag {

+ 4 - 4
src/defaults.ts

@@ -3,7 +3,7 @@ import type { MarkedOptions } from './MarkedOptions.ts';
 /**
 /**
  * Gets the original marked default options.
  * Gets the original marked default options.
  */
  */
-export function _getDefaults(): MarkedOptions {
+export function _getDefaults<ParserOutput = string, RendererOutput = string>(): MarkedOptions<ParserOutput, RendererOutput> {
   return {
   return {
     async: false,
     async: false,
     breaks: false,
     breaks: false,
@@ -17,9 +17,9 @@ export function _getDefaults(): MarkedOptions {
     walkTokens: null,
     walkTokens: null,
   };
   };
 }
 }
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export let _defaults: MarkedOptions<any, any> = _getDefaults();
 
 
-export let _defaults = _getDefaults();
-
-export function changeDefaults(newDefaults: MarkedOptions) {
+export function changeDefaults<ParserOutput = string, RendererOutput = string>(newDefaults: MarkedOptions<ParserOutput, RendererOutput>) {
   _defaults = newDefaults;
   _defaults = newDefaults;
 }
 }

+ 16 - 2
test/types/marked.ts

@@ -3,8 +3,8 @@ import { marked } from 'marked';
 
 
 // other exports
 // other exports
 
 
-import { Lexer, Parser, Tokenizer, Renderer, TextRenderer } from 'marked';
-import type { Tokens, MarkedExtension, TokenizerAndRendererExtension, Token ,TokenizerExtension, MarkedOptions, TokensList, RendererExtension } from 'marked';
+import { Lexer, Parser, Tokenizer, Renderer, TextRenderer, Marked } from 'marked';
+import type { Tokens, MarkedExtension, TokenizerAndRendererExtension, Token ,TokenizerExtension, MarkedOptions, TokensList, RendererExtension, RendererObject } from 'marked';
 
 
 const tokenizer = new marked.Tokenizer();
 const tokenizer = new marked.Tokenizer();
 
 
@@ -398,3 +398,17 @@ marked.use({
   tokenizer: undefined,
   tokenizer: undefined,
   walkTokens: undefined,
   walkTokens: undefined,
 });
 });
+
+const markedNumber = new Marked<number, number>();
+const rendererNumber = {
+  html() {
+    return 1;
+  }
+};
+function parserNumber(tokens: Token[], options?: MarkedOptions<number, number>){
+    return 1;
+};
+markedNumber.use({ renderer: rendererNumber, hooks: { provideParser() { return parserNumber; } } });
+
+const num: number = await markedNumber.parse('');
+console.log(num);