highlight.js icon indicating copy to clipboard operation
highlight.js copied to clipboard

TS1256: A rest element must be last in a tuple type

Open ethan-fraser opened this issue 2 years ago • 9 comments

Describe the issue/behavior that seems buggy When compiling with tsc v4.9.3, it fails with the following error:

> tsc

node_modules/highlight.js/types/index.d.ts:55:54 - error TS1256: A rest element must be last in a tuple type.

55             either: (...args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) => string,
                                                         ~~~~~~~~~~~~~~~~~~~~~~

Found 1 error.

Expected behavior Compiling should not fail.

Additional context At index.d.ts:55, if you swap RegexEitherOptions and ...(RegExp | string)[], the error is resolved.

i.e.

<<<<<<
            either: (...args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) => string,
======
            either: (...args: (RegExp | string)[] | [RegexEitherOptions, ...(RegExp | string)[]]) => string,
>>>>>>

ethan-fraser avatar Jan 30 '23 21:01 ethan-fraser

It's not supposed to be a tuple, it's supposed to be any number of arguments followed by RegexEitherOptions

joshgoebel avatar Jan 31 '23 00:01 joshgoebel

Same here with tsc 3.9.7.

RicardoPhilippe avatar Feb 05 '23 23:02 RicardoPhilippe

Can someone recommend the right way to type this?

https://github.com/highlightjs/highlight.js/blob/main/src/lib/regex.js#L77

The type of args is literally (RegExp | string)[] except the last params can optinally be a RegexEitherOptions.

joshgoebel avatar Feb 09 '23 02:02 joshgoebel

type RegexEitherOptions = 
{
    more: boolean;
}

    interface PublicApi {
        regex: {
            concat: (...args: (RegExp | string)[]) => string,
            lookahead: (re: RegExp | string) => string,
            either: (...args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) => string,
            optional: (re: RegExp | string) => string,
            anyNumberOfTimes: (re: RegExp | string) => string
        }
    }

Seems to work in the 4.9.5 playground without issue?

joshgoebel avatar Mar 06 '23 00:03 joshgoebel

This snippet also seems to work just fine:

    type RegexEitherOptions = {
        capture?: boolean
    }


export function either(...args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) {
  const opts : RegexEitherOptions = stripOptionsFromArgs(args);
  args = args as (RegExp | string)[];
  const joined = '('
    + (opts.capture ? "" : "?:")
    + args.map((x) => source(x)).join("|") + ")";
  return joined;
}

function stripOptionsFromArgs(args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) : RegexEitherOptions {
  const opts = args[args.length - 1] as RegexEitherOptions;

  if (typeof opts === 'object' && opts.constructor === Object) {
    args.splice(args.length - 1, 1);
    return opts;
  } else {
    return {};
  }
}

export function source(re : RegExp | string) {
  if (!re) return null;
  if (typeof re === "string") return re;

  return re.source;
}

joshgoebel avatar Mar 06 '23 00:03 joshgoebel

I'm sure this comment won't endear me to the rest of the Typescript community, but usability is more important than technical compliance. Perhaps you could use an interim any.

PeterWone avatar Mar 06 '23 05:03 PeterWone

So right now I can't reproduce this error with the web version - is it no longer an issue in the latest TS versions?

joshgoebel avatar Mar 06 '23 23:03 joshgoebel

VS Code 1.76 uses 4.95 but I can't tell from here what version of tsc is used by gulp in my CICD, I'll have to look into that after work.

PeterWone avatar Mar 07 '23 04:03 PeterWone

I only write the types to use the linting features in VS Code... so if it worked when I wrote it - and evidently it works in the latest tsc - does that make 4.95 sound like perhaps a bug or regression?

joshgoebel avatar Mar 07 '23 04:03 joshgoebel