• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / js-slang / 23995741899

05 Apr 2026 06:14AM UTC coverage: 77.093% (+0.002%) from 77.091%
23995741899

push

github

web-flow
Upgrade to TypeScript 6 and Prettier improvements (#1936)

* Upgrade TypeScript to v6

* Fix import source

* Fix tsconfig

* Fix preexisting type errors

* Remove scm-slang

* Bump node types

* Fix tsconfig

* Fix node types specifier

* Enable trailing commas

* Enable semicolons

* Check and commit files with changed line numbers

* Update Yarn to 4.13.0

* Remove unneeded sicp package deps

3112 of 4282 branches covered (72.68%)

Branch coverage included in aggregate %.

3761 of 5218 new or added lines in 152 files covered. (72.08%)

26 existing lines in 9 files now uncovered.

7136 of 9011 relevant lines covered (79.19%)

175254.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.69
/src/parser/source/typed/typeParser.ts
1
// Code taken from https://github.com/patternfly/patternfly-org/blob/main/packages/ast-helpers/acorn-typescript.js
2
// Some cases such as arrow function expressions are not properly handled
3
import { getLineInfo, Parser, tokTypes } from 'acorn';
4

5
// Taken from https://github.com/acornjs/acorn/blob/6770c2ecbf8e01470f6c9a2f59c786f014045baf/acorn/src/whitespace.js#L4C1-L5C1
6
const lineBreak = /\r\n?|\n|\u2028|\u2029/;
65✔
7

8
class DestructuringErrors {
9
  shorthandAssign: number;
10
  trailingComma: number;
11
  parenthesizedAssign: number;
12
  parenthesizedBind: number;
13
  doubleProto: number;
14
  constructor() {
15
    this.shorthandAssign =
67✔
16
      this.trailingComma =
17
      this.parenthesizedAssign =
18
      this.parenthesizedBind =
19
      this.doubleProto =
20
        -1;
21
  }
22
}
23

24
const tsPredefinedType = {
65✔
25
  any: 'TSAnyKeyword',
26
  bigint: 'TSBigIntKeyword',
27
  boolean: 'TSBooleanKeyword',
28
  never: 'TSNeverKeyword',
29
  null: 'TSNullKeyword',
30
  number: 'TSNumberKeyword',
31
  object: 'TSObjectKeyword',
32
  string: 'TSStringKeyword',
33
  symbol: 'TSSymbolKeyword',
34
  undefined: 'TSUndefinedKeyword',
35
  unknown: 'TSUnknownKeyword',
36
  void: 'TSVoidKeyword',
37
};
38

39
const tsDeclaration = {
65✔
40
  interface: 1,
41
  type: 2,
42
  enum: 4,
43
  declare: 8,
44
};
45

46
const tsTypeOperator = {
65✔
47
  typeof: 1,
48
  keyof: 2,
49
  infer: 4,
50
};
51

52
const tsExprMarkup = {
65✔
53
  as: 1,
54
  '!': 2,
55
};
56

57
const tsPlugin = (BaseParser: any) => {
65✔
58
  return class extends BaseParser {
65✔
59
    constructor(...args: any) {
60
      super(...args);
283✔
61
      // Allow 'interface'
62
      this.reservedWords = /^(?:enum)$/;
283✔
63
      this.reservedWordsStrict = this.reservedWords;
283✔
64
    }
65

66
    finishNode(node: any, type: string) {
67
      if (type.startsWith('TS')) {
5,394✔
68
        // Hack to not need acorn-walk to detect TS
69
        this.options.sourceType = 'ts';
1,663✔
70
      }
71
      return this.finishNodeAt.call(this, node, type, this.lastTokEnd, this.lastTokEndLoc);
5,394✔
72
    }
73

74
    computeLocByOffset(offset: any) {
75
      // If `locations` option is off, do nothing for saving performance.
76
      if (this.options.locations) {
102✔
77
        return getLineInfo(this.input, offset);
100✔
78
      } else {
79
        return;
2✔
80
      }
81
    }
82

83
    startNodeAtNode(node: { start: any }) {
84
      return this.startNodeAt(node.start, this.computeLocByOffset(node.start));
102✔
85
    }
86

87
    tsPreparePreview() {
88
      const {
89
        pos,
90
        curLine,
91
        type,
92
        value,
93
        end,
94
        start,
95
        endLoc,
96
        startLoc,
97
        scopeStack,
98
        lastTokStartLoc,
99
        lastTokEndLoc,
100
        lastTokEnd,
101
        lastTokStart,
102
        context,
103
      } = this;
36✔
104
      return () => {
36✔
105
        this.pos = pos;
36✔
106
        this.curLine = curLine;
36✔
107
        this.type = type;
36✔
108
        this.value = value;
36✔
109
        this.end = end;
36✔
110
        this.start = start;
36✔
111
        this.endLoc = endLoc;
36✔
112
        this.startLoc = startLoc;
36✔
113
        this.scopeStack = scopeStack;
36✔
114
        this.lastTokStartLoc = lastTokStartLoc;
36✔
115
        this.lastTokEndLoc = lastTokEndLoc;
36✔
116
        this.lastTokEnd = lastTokEnd;
36✔
117
        this.lastTokStart = lastTokStart;
36✔
118
        this.context = context;
36✔
119
      };
120
    }
121

122
    _isStartOfTypeParameters() {
123
      return this.value && this.value.charCodeAt(0) === 60; // <
1,628✔
124
    }
125

126
    _isEndOfTypeParameters() {
127
      return this.value && this.value.charCodeAt(0) === 62; // >
194✔
128
    }
129

130
    _hasPrecedingLineBreak() {
NEW
131
      return lineBreak.test(this.input.slice(this.lastTokEnd, this.start));
×
132
    }
133

134
    // Studied from Babel
135
    parseExpressionStatement(node: any, expr: any) {
136
      return expr.type === 'Identifier'
117✔
137
        ? this._parseTSDeclaration(node, expr)
138
        : super.parseExpressionStatement(node, expr);
139
    }
140

141
    parseBindingAtom() {
142
      const node = super.parseBindingAtom();
476✔
143
      if (this.eat(tokTypes.colon)) {
476✔
144
        node.typeAnnotation = this.parseTSTypeAnnotation(false);
438✔
145
        node.end = node.typeAnnotation.end;
438✔
146
        if (this.options.locations) {
438✔
147
          node.loc.end = node.typeAnnotation.loc.end;
434✔
148
        }
149
      }
150
      return node;
476✔
151
    }
152

153
    parseMaybeDefault(
154
      startPos: any,
155
      startLoc: any,
156
      left: {
157
        optional: boolean;
158
        typeAnnotation: { end: any; loc: { end: any } };
159
        end: any;
160
        loc: { end: any };
161
      },
162
    ) {
163
      if (!left) {
81!
164
        left = this.parseBindingAtom();
81✔
165
        if (this.eat(tokTypes.question)) {
81!
NEW
166
          left.optional = true;
×
167
        }
168
        // `parseBindingAtom` is executed,
169
        // so we need to check type annotation again.
170
        if (this.eat(tokTypes.colon)) {
81!
NEW
171
          left.typeAnnotation = this.parseTSTypeAnnotation(false);
×
NEW
172
          left.end = left.typeAnnotation.end;
×
173
          if (this.options.locations) {
×
NEW
174
            left.loc.end = left.typeAnnotation.loc.end;
×
175
          }
176
        }
177
      }
178
      return super.parseMaybeDefault(startPos, startLoc, left);
81✔
179
    }
180

181
    parseMaybeAssign(
182
      noIn: boolean,
183
      refDestructuringErrors: any,
184
      afterLeftParse: (item: any) => any,
185
    ) {
186
      let node = super.parseMaybeAssign(noIn, refDestructuringErrors, afterLeftParse);
1,134✔
187
      node = this._parseMaybeTSExpression(node);
1,134✔
188
      return node;
1,134✔
189
    }
190

191
    parseFunctionParams(node: { typeParameters: any }) {
192
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
92✔
193
      return super.parseFunctionParams(node);
92✔
194
    }
195

196
    parseFunctionBody(node: { returnType: any }, isArrowFunction: any) {
197
      // I know, return type doesn't belong to function body,
198
      // but this will be less hacky.
199
      if (this.eat(tokTypes.colon)) {
117✔
200
        node.returnType = this.parseTSTypeAnnotation(false);
46✔
201
      }
202
      super.parseFunctionBody(node, isArrowFunction);
117✔
203
    }
204

205
    parseParenAndDistinguishExpression(canBeArrow: any) {
206
      const startPos = this.start;
67✔
207
      const startLoc = this.startLoc;
67✔
208
      const allowTrailingComma = this.options.ecmaVersion >= 8;
67✔
209
      let val;
210
      if (this.options.ecmaVersion >= 6) {
67!
211
        this.next();
67✔
212

213
        const innerStartPos = this.start,
67✔
214
          innerStartLoc = this.startLoc;
67✔
215
        const exprList = [];
67✔
216
        let first = true,
67✔
217
          lastIsComma = false;
67✔
218
        const refDestructuringErrors = new DestructuringErrors(),
67✔
219
          oldYieldPos = this.yieldPos,
67✔
220
          oldAwaitPos = this.awaitPos;
67✔
221
        let spreadStart;
222
        this.yieldPos = 0;
67✔
223
        this.awaitPos = 0;
67✔
224
        // Do not save awaitIdentPos to allow checking awaits nested in parameters
225
        while (this.type !== tokTypes.parenR) {
67✔
226
          if (first) {
89✔
227
            first = false;
63✔
228
          } else {
229
            this.expect(tokTypes.comma);
26✔
230
          }
231
          if (allowTrailingComma && this.afterTrailingComma(tokTypes.parenR, true)) {
89!
NEW
232
            lastIsComma = true;
×
NEW
233
            break;
×
234
          } else if (this.type === tokTypes.ellipsis) {
89!
NEW
235
            spreadStart = this.start;
×
NEW
236
            exprList.push(this.parseParenItem(this.parseRestBinding()));
×
237
            if (this.type === tokTypes.comma)
×
NEW
238
              this.raise(this.start, 'Comma is not permitted after the rest element');
×
NEW
239
            break;
×
240
          } else {
241
            exprList.push(
89✔
242
              this.parseMaybeAssign(false, refDestructuringErrors, this.parseParenItem),
243
            );
244
          }
245
          if (this.type === tokTypes.colon) {
89✔
246
            this.parseTSTypeAnnotation(); // Part I added
56✔
247
          }
248
        }
249
        const innerEndPos = this.start;
67✔
250
        const innerEndLoc = this.startLoc;
67✔
251
        this.expect(tokTypes.parenR);
67✔
252

253
        if (canBeArrow && !this.canInsertSemicolon()) {
67!
254
          const branch = this._branch();
67✔
255
          try {
67✔
256
            if (branch.parseTSTypeAnnotation() && branch.eat(tokTypes.arrow)) {
67✔
257
              this.parseTSTypeAnnotation(); // throw away type
26✔
258
            }
259
          } catch {}
260
          if (this.eat(tokTypes.arrow)) {
67✔
261
            this.checkPatternErrors(refDestructuringErrors, false);
53✔
262
            this.checkYieldAwaitInDefaultParams();
53✔
263
            this.yieldPos = oldYieldPos;
53✔
264
            this.awaitPos = oldAwaitPos;
53✔
265
            return this.parseParenArrowList(startPos, startLoc, exprList);
53✔
266
          }
267
        }
268

269
        if (!exprList.length || lastIsComma) this.unexpected(this.lastTokStart);
14!
270
        if (spreadStart) this.unexpected(spreadStart);
14!
271
        this.checkExpressionErrors(refDestructuringErrors, true);
14✔
272
        this.yieldPos = oldYieldPos || this.yieldPos;
14✔
273
        this.awaitPos = oldAwaitPos || this.awaitPos;
67✔
274

275
        if (exprList.length > 1) {
67!
NEW
276
          val = this.startNodeAt(innerStartPos, innerStartLoc);
×
NEW
277
          val.expressions = exprList;
×
NEW
278
          this.finishNodeAt(val, 'SequenceExpression', innerEndPos, innerEndLoc);
×
279
        } else {
280
          val = exprList[0];
14✔
281
        }
282
      } else {
NEW
283
        val = this.parseParenExpression();
×
284
      }
285

286
      if (this.options.preserveParens) {
14!
NEW
287
        const par = this.startNodeAt(startPos, startLoc);
×
NEW
288
        par.expression = val;
×
NEW
289
        return this.finishNode(par, 'ParenthesizedExpression');
×
290
      } else {
291
        return val;
14✔
292
      }
293
    }
294

295
    // Fix ambiguity between BinaryExpressions and TSCallExpressions
296
    parseSubscript(base: { typeParameters: any }) {
297
      const branch = this._branch();
1,393✔
298
      if (this._isStartOfTypeParameters()) {
1,393✔
299
        // <
300
        try {
6✔
301
          // will throw if no matching >
302
          const typeParameters = branch.parseTSTypeParameterInstantiation();
6✔
303
          if (typeParameters && branch.eat(tokTypes.parenL)) {
6!
304
            // Update parser to match branch
NEW
305
            base.typeParameters = this.parseTSTypeParameterInstantiation();
×
306
          }
307
        } catch {}
308
      }
309

310
      return super.parseSubscript.apply(this, arguments);
1,393✔
311
    }
312

313
    parseExpression() {
314
      const parenthesized = this.type === tokTypes.parenL,
229✔
315
        parenStart = parenthesized ? this.start : -1;
229✔
316
      let expr = super.parseExpression();
229✔
317

318
      if (parenthesized) {
229✔
319
        expr.extra = { parenthesized, parenStart };
15✔
320
        return expr;
15✔
321
      }
322

323
      expr = this._parseMaybeTSExpression(expr);
214✔
324
      return expr;
214✔
325
    }
326

327
    parseParenItem(item: any) {
328
      item = super.parseParenItem(item);
89✔
329
      item = this._parseMaybeTSExpression(item);
89✔
330
      return item;
89✔
331
    }
332

333
    parseTSTypeAnnotation(eatColon = true) {
667✔
334
      if (eatColon) {
667✔
335
        this.expect(tokTypes.colon);
149✔
336
      }
337
      const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
626✔
338
      this._parseTSTypeAnnotation(node);
626✔
339
      return this.finishNode(node, 'TSTypeAnnotation');
626✔
340
    }
341

342
    _parseTSType() {
343
      const node = this._parseNonConditionalType();
747✔
344
      if (this.type === tokTypes._extends && !this._hasPrecedingLineBreak()) {
747!
NEW
345
        return this.parseTSConditionalType(node);
×
346
      }
347
      return node;
747✔
348
    }
349

350
    _parseTSTypeAnnotation(node: { typeAnnotation: any }) {
351
      node.typeAnnotation = this._parseTSType();
662✔
352
    }
353

354
    _parsePrimaryType() {
355
      let node;
356
      switch (this.type) {
798!
357
        case tokTypes.name:
358
          node =
751✔
359
            this.value in tsPredefinedType
751✔
360
              ? this.parseTSPredefinedType()
361
              : this.parseTSTypeReference();
362
          break;
751✔
363
        case tokTypes.braceL:
NEW
364
          node = this.parseTSTypeLiteral();
×
NEW
365
          break;
×
366
        case tokTypes._void:
367
        case tokTypes._null:
368
          node = this.parseTSPredefinedType();
17✔
369
          break;
17✔
370
        case tokTypes.parenL:
NEW
371
          node = this.parseTSParenthesizedType();
×
NEW
372
          break;
×
373
        case tokTypes.bracketL:
NEW
374
          node = this.parseTSTupleType();
×
NEW
375
          break;
×
376
        case tokTypes.num:
377
        case tokTypes.string:
378
        case tokTypes._true:
379
        case tokTypes._false:
380
          node = this.parseTSLiteralType(this.type);
30✔
381
          break;
30✔
382
        case tokTypes._import:
NEW
383
          node = this.parseTSImportType(false);
×
NEW
384
          break;
×
385
        default:
NEW
386
          return;
×
387
      }
388

389
      while (this.type === tokTypes.bracketL) {
798✔
390
        node = this._parseMaybeTSArrayType(node);
19✔
391
      }
392

393
      return node;
798✔
394
    }
395

396
    _parseNonConditionalType() {
397
      let node;
398
      switch (this.type) {
747!
399
        case tokTypes.name:
400
          switch (tsTypeOperator[this.value as keyof typeof tsTypeOperator]) {
677!
401
            case tsTypeOperator.infer:
NEW
402
              node = this.parseTSInferType();
×
NEW
403
              break;
×
404
            case tsTypeOperator.keyof:
NEW
405
              node = this.parseTSKeyofType();
×
NEW
406
              break;
×
407
            default:
408
              node = this._parseTSUnionTypeOrIntersectionType();
677✔
409
          }
410
          break;
677✔
411
        case tokTypes._new:
NEW
412
          node = this.parseTSConstructorType();
×
NEW
413
          break;
×
414
        case tokTypes.parenL:
415
          const recover = this.tsPreparePreview();
36✔
416
          const isStartOfTSFunctionType = this._isStartOfTSFunctionType();
36✔
417
          recover();
36✔
418
          node = isStartOfTSFunctionType
36✔
419
            ? this.parseTSFunctionType()
420
            : this.parseTSParenthesizedType();
421
          break;
36✔
422
        case tokTypes.relational:
NEW
423
          node = this._isStartOfTypeParameters() ? this.parseTSFunctionType() : this.unexpected();
×
NEW
424
          break;
×
425
        case tokTypes._typeof:
NEW
426
          node = this.parseTSTypeofType();
×
NEW
427
          break;
×
428
        default:
429
          node = this._parseTSUnionTypeOrIntersectionType();
34✔
430
          break;
34✔
431
      }
432
      return node || this.unexpected();
747!
433
    }
434

435
    _parseTSDeclaration(node: any, expr: { name: string | number }) {
436
      const val = tsDeclaration[expr.name as keyof typeof tsDeclaration];
30✔
437
      switch (val) {
30!
438
        case tsDeclaration.interface:
439
          if (this.type === tokTypes.name) {
×
NEW
440
            return this.parseTSInterfaceDeclaration();
×
441
          }
NEW
442
          break;
×
443
        case tsDeclaration.type:
444
          if (this.type === tokTypes.name) {
25!
445
            return this.parseTSTypeAliasDeclaration();
25✔
446
          }
NEW
447
          break;
×
448
        default:
449
          break;
5✔
450
      }
451
      return super.parseExpressionStatement(node, expr);
5✔
452
    }
453

454
    parseTSTypeReference() {
455
      const node = this.startNode();
84✔
456
      let typeName = this.parseIdent();
84✔
457
      if (this.type === tokTypes.dot) {
84!
NEW
458
        typeName = this.parseTSQualifiedName(typeName);
×
459
      }
460
      node.typeName = typeName;
84✔
461
      if (this._isStartOfTypeParameters()) {
84✔
462
        node.typeParameters = this.parseTSTypeParameterInstantiation();
58✔
463
      }
464
      this.finishNode(node, 'TSTypeReference');
84✔
465
      return node;
84✔
466
    }
467

468
    parseTSPredefinedType() {
469
      const node = this.startNode();
684✔
470
      const keyword = this.value;
684✔
471
      this.next();
684✔
472
      this.finishNode(node, tsPredefinedType[keyword as keyof typeof tsPredefinedType]);
684✔
473
      return node;
684✔
474
    }
475

476
    parseTSLiteralType(tokType: any) {
477
      const node = this.startNode();
30✔
478
      const literal = this.parseLiteral(this.value);
30✔
479
      if (tokType === tokTypes._true || tokType === tokTypes._false) {
30✔
480
        literal.value = tokType === tokTypes._true;
6✔
481
      }
482
      node.literal = literal;
30✔
483
      return this.finishNode(node, 'TSLiteralType');
30✔
484
    }
485

486
    parseTSTupleType() {
NEW
487
      const node = this.startNode();
×
NEW
488
      const elementTypes = [];
×
NEW
489
      this.eat(tokTypes.bracketL);
×
NEW
490
      let first = true;
×
491
      while (!this.eat(tokTypes.bracketR)) {
×
492
        if (first) {
×
NEW
493
          first = false;
×
494
        } else {
NEW
495
          this.expect(tokTypes.comma);
×
496
        }
497
        switch (this.type) {
×
498
          case tokTypes.name:
NEW
499
            const elem = this.parseTSTypeReference();
×
500
            if (this.type === tokTypes.question) {
×
NEW
501
              elementTypes.push(this.parseTSOptionalType(elem));
×
502
            } else {
NEW
503
              elementTypes.push(elem);
×
504
            }
NEW
505
            break;
×
506
          case tokTypes.ellipsis:
NEW
507
            elementTypes.push(this.parseTSRestType());
×
NEW
508
            break;
×
509
          case tokTypes.bracketR:
NEW
510
            break;
×
511
          default:
NEW
512
            this.unexpected();
×
513
        }
514
      }
NEW
515
      node.elementTypes = elementTypes;
×
NEW
516
      return this.finishNode(node, 'TSTupleType');
×
517
    }
518

519
    parseTSOptionalType(typeRef: any) {
NEW
520
      const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
×
NEW
521
      this.expect(tokTypes.question);
×
NEW
522
      node.typeAnnotation = typeRef;
×
NEW
523
      return this.finishNode(node, 'TSOptionalType');
×
524
    }
525

526
    parseTSRestType() {
NEW
527
      const node = this.startNode();
×
NEW
528
      this.expect(tokTypes.ellipsis);
×
NEW
529
      this._parseTSTypeAnnotation(node);
×
NEW
530
      return this.finishNode(node, 'TSRestType');
×
531
    }
532

533
    _parseMaybeTSArrayType(prev: any): any {
534
      const node = this.startNodeAtNode(prev);
19✔
535
      this.expect(tokTypes.bracketL);
19✔
536
      if (this.eat(tokTypes.bracketR)) {
19!
537
        return this.parseTSArrayType(node, prev);
19✔
538
      }
NEW
539
      return this.parseTSIndexedAccessType(node, prev);
×
540
    }
541

542
    parseTSArrayType(node: { elementType: any }, elementType: any) {
543
      node.elementType = elementType;
19✔
544
      return this.finishNode(node, 'TSArrayType');
19✔
545
    }
546

547
    parseTSIndexedAccessType(node: { objectType: any; indexType: any }, objectType: any) {
NEW
548
      node.objectType = objectType;
×
NEW
549
      node.indexType = this._parseTSType();
×
NEW
550
      this.expect(tokTypes.bracketR);
×
551
      if (this.type === tokTypes.bracketL) {
×
NEW
552
        return this._parseMaybeTSArrayType(node);
×
553
      }
NEW
554
      return this.finishNode(node, 'TSIndexedAccessType');
×
555
    }
556

557
    _isStartOfTSFunctionType() {
558
      this.nextToken();
36✔
559
      switch (this.type) {
36!
560
        case tokTypes.parenR:
561
        case tokTypes.ellipsis:
562
          return true;
2✔
563
        case tokTypes.name:
564
        case tokTypes._this:
565
          this.nextToken();
34✔
566
          switch (this.type) {
34!
567
            case tokTypes.colon:
568
            case tokTypes.comma:
569
            case tokTypes.question:
570
              return true;
32✔
571
            case tokTypes.parenR:
NEW
572
              this.nextToken();
×
NEW
573
              return this.type === tokTypes.arrow;
×
574
            default:
575
              return false;
2✔
576
          }
577
        case tokTypes.braceL:
578
        case tokTypes.bracketL: {
579
          if (this.type === tokTypes.braceL) {
×
NEW
580
            this.parseObj(/* isPattern */ true);
×
581
          } else {
NEW
582
            this.parseBindingAtom();
×
583
          }
584

585
          switch (this.type) {
×
586
            case tokTypes.colon:
587
            case tokTypes.comma:
588
            case tokTypes.question:
NEW
589
              return true;
×
590
            case tokTypes.parenR:
NEW
591
              this.nextToken();
×
NEW
592
              return this.type === tokTypes.arrow;
×
593
            default:
NEW
594
              return false;
×
595
          }
596
        }
597
        default:
NEW
598
          return false;
×
599
      }
600
    }
601

602
    parseTSFunctionType() {
603
      const node = this.startNode();
34✔
604
      const temp = Object.create(null);
34✔
605
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
34✔
606
      this.parseFunctionParams(temp);
34✔
607
      node.parameters = temp.params;
34✔
608
      this.expect(tokTypes.arrow);
34✔
609
      node.typeAnnotation = this.parseTSTypeAnnotation(false);
34✔
610
      return this.finishNode(node, 'TSFunctionType');
34✔
611
    }
612

613
    parseTSParenthesizedType() {
614
      const node = this.startNode();
2✔
615
      this.expect(tokTypes.parenL);
2✔
616
      this._parseTSTypeAnnotation(node);
2✔
617
      this.expect(tokTypes.parenR);
2✔
618
      while (this.eat(tokTypes.bracketL)) {
2✔
619
        this.expect(tokTypes.bracketR);
3✔
620
      }
621
      return this.finishNode(node, 'TSParenthesizedType');
2✔
622
    }
623

624
    parseTSUnionType(first: any) {
625
      const node = first ? this.startNodeAtNode(first) : this.startNode();
74!
626
      const types = [];
74✔
627
      if (first) types.push(first);
74!
628
      while (this.eat(tokTypes.bitwiseOR)) {
74✔
629
        types.push(this._parseTSIntersectionTypeOrPrimaryType());
87✔
630
      }
631
      if (types.length === 1) {
74!
NEW
632
        return first;
×
633
      }
634
      node.types = types;
74✔
635
      return this.finishNode(node, 'TSUnionType');
74✔
636
    }
637

638
    parseTSIntersectionType(first: any) {
NEW
639
      const node = first ? this.startNodeAtNode(first) : this.startNode();
×
NEW
640
      const types = [];
×
NEW
641
      if (first) types.push(first);
×
642
      while (this.eat(tokTypes.bitwiseAND)) {
×
NEW
643
        types.push(this._parsePrimaryType());
×
644
      }
645
      if (types.length === 1) {
×
NEW
646
        return first;
×
647
      }
NEW
648
      node.types = types;
×
NEW
649
      return this.finishNode(node, 'TSIntersectionType');
×
650
    }
651

652
    _parseTSIntersectionTypeOrPrimaryType() {
653
      this.eat(tokTypes.bitwiseAND);
798✔
654
      const node = this._parsePrimaryType();
798✔
655
      if (this.type === tokTypes.bitwiseAND) {
798!
NEW
656
        return this.parseTSIntersectionType(node);
×
657
      }
658
      return node;
798✔
659
    }
660

661
    _parseTSUnionTypeOrIntersectionType() {
662
      this.eat(tokTypes.bitwiseOR);
711✔
663
      const node = this._parseTSIntersectionTypeOrPrimaryType();
711✔
664
      if (this.type === tokTypes.bitwiseOR) {
711✔
665
        return this.parseTSUnionType(node);
74✔
666
      }
667
      return node;
637✔
668
    }
669

670
    parseTSConditionalType(checkType: any) {
NEW
671
      const node = this.startNodeAtNode(checkType);
×
NEW
672
      node.checkType = checkType;
×
NEW
673
      this.expect(tokTypes._extends);
×
NEW
674
      node.extendsType = this._parseNonConditionalType();
×
NEW
675
      this.expect(tokTypes.question);
×
NEW
676
      node.trueType = this._parseNonConditionalType();
×
NEW
677
      this.expect(tokTypes.colon);
×
NEW
678
      node.falseType = this._parseNonConditionalType();
×
NEW
679
      return this.finishNode(node, 'TSConditionalType');
×
680
    }
681

682
    parseTSInferType() {
NEW
683
      const node = this.startNode();
×
NEW
684
      this.next();
×
NEW
685
      node.typeParameter = this.parseTSTypeParameter();
×
NEW
686
      return this.finishNode(node, 'TSInferType');
×
687
    }
688

689
    parseTSKeyofType() {
NEW
690
      const node = this.startNode();
×
NEW
691
      this.next();
×
NEW
692
      node.typeAnnotation = this.parseTSTypeAnnotation(false);
×
NEW
693
      return this.finishNode(node, 'TSTypeOperator');
×
694
    }
695

696
    parseTSTypeQuery() {
NEW
697
      const node = this.startNode();
×
NEW
698
      this.next();
×
NEW
699
      node.exprName = this.parseIdent();
×
NEW
700
      return this.finishNode(node, 'TSTypeQuery');
×
701
    }
702

703
    parseTSTypeofType() {
NEW
704
      const typeQuery = this.parseTSTypeQuery();
×
705
      if (this.eat(tokTypes.bracketL)) {
×
NEW
706
        const node = this.startNode();
×
NEW
707
        return this.parseTSIndexedAccessType(node, typeQuery);
×
708
      }
NEW
709
      return typeQuery;
×
710
    }
711

712
    parseTSImportType(isTypeOf: boolean) {
NEW
713
      const node = this.startNode();
×
NEW
714
      node.isTypeOf = isTypeOf;
×
NEW
715
      this.expect(tokTypes._import);
×
NEW
716
      this.expect(tokTypes.parenL);
×
NEW
717
      node.parameter = this.parseTSLiteralType(this.type);
×
NEW
718
      this.expect(tokTypes.parenR);
×
719
      if (this.eat(tokTypes.dot)) {
×
NEW
720
        let qualifier = this.parseIdent();
×
721
        if (this.type === tokTypes.dot) {
×
NEW
722
          qualifier = this.parseTSQualifiedName(qualifier);
×
723
        }
NEW
724
        node.qualifier = qualifier;
×
725
      }
NEW
726
      return this.finishNode(node, 'TSImportType');
×
727
    }
728

729
    parseTSQualifiedName(left: any) {
NEW
730
      let node = this.startNodeAtNode(left);
×
NEW
731
      node.left = left;
×
NEW
732
      this.expect(tokTypes.dot);
×
NEW
733
      node.right = this.parseIdent();
×
NEW
734
      node = this.finishNode(node, 'TSQualifiedName');
×
735
      if (this.type === tokTypes.dot) {
×
NEW
736
        node = this.parseTSQualifiedName(node);
×
737
      }
NEW
738
      return node;
×
739
    }
740

741
    parseTSConstructorType() {
NEW
742
      const node = this.startNode();
×
NEW
743
      this.expect(tokTypes._new);
×
NEW
744
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
×
NEW
745
      this.expect(tokTypes.parenL);
×
NEW
746
      node.parameters = this.parseBindingList(
×
747
        tokTypes.parenR,
748
        false,
749
        this.options.ecmaVersion >= 8,
750
      );
NEW
751
      this.expect(tokTypes.arrow);
×
NEW
752
      node.typeAnnotation = this.parseTSTypeAnnotation(false);
×
NEW
753
      return this.finishNode(node, 'TSConstructorType');
×
754
    }
755

756
    parseTSConstructSignatureDeclaration() {
NEW
757
      const node = this.startNode();
×
NEW
758
      this.expect(tokTypes._new);
×
NEW
759
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
×
NEW
760
      this.expect(tokTypes.parenL);
×
NEW
761
      node.parameters = this.parseBindingList(
×
762
        tokTypes.parenR,
763
        false,
764
        this.options.ecmaVersion >= 8,
765
      );
766
      if (this.eat(tokTypes.colon)) {
×
NEW
767
        node.typeAnnotation = this.parseTSTypeAnnotation(false);
×
768
      }
NEW
769
      return this.finishNode(node, 'TSConstructSignatureDeclaration');
×
770
    }
771

772
    parseTSTypeLiteral() {
NEW
773
      return this._parseObjectLikeType('TSTypeLiteral', 'members');
×
774
    }
775

776
    parseTSTypeAliasDeclaration() {
777
      const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
25✔
778
      node.id = this.parseIdent();
25✔
779
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
25✔
780
      this.expect(tokTypes.eq);
25✔
781
      this._parseTSTypeAnnotation(node);
25✔
782
      this.semicolon();
25✔
783
      return this.finishNode(node, 'TSTypeAliasDeclaration');
25✔
784
    }
785

786
    parseTSInterfaceDeclaration() {
NEW
787
      const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
×
NEW
788
      node.id = this.parseIdent();
×
NEW
789
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
×
790
      if (this.eat(tokTypes._extends)) {
×
NEW
791
        const heritage = [];
×
792
        do {
×
NEW
793
          heritage.push(this.parseTSExpressionWithTypeArguments());
×
794
        } while (this.eat(tokTypes.comma));
NEW
795
        node.heritage = heritage;
×
796
      }
NEW
797
      node.body = this._parseObjectLikeType('TSInterfaceBody', 'body');
×
NEW
798
      this.semicolon();
×
NEW
799
      return this.finishNode(node, 'TSInterfaceDeclaration');
×
800
    }
801

802
    parseTSExpressionWithTypeArguments() {
NEW
803
      const node = this.startNode();
×
NEW
804
      let expr = this.parseIdent();
×
805
      if (this.eat(tokTypes.dot)) {
×
NEW
806
        expr = this.parseTSQualifiedName(expr);
×
807
      }
NEW
808
      node.expr = expr;
×
809
      if (this._isStartOfTypeParameters()) {
×
NEW
810
        const typeParameters = this.parseTSTypeParameterInstantiation();
×
NEW
811
        node.typeParameters = typeParameters;
×
NEW
812
        node.end = typeParameters.end;
×
813
        if (this.options.locations) {
×
NEW
814
          node.loc.end = typeParameters.loc.end;
×
815
        }
816
      }
NEW
817
      return this.finishNode(node, 'TSExpressionWithTypeArguments');
×
818
    }
819

820
    parseTSTypeParameter() {
821
      const node = this.startNode();
10✔
822
      if (this.type === tokTypes.name) {
10!
823
        node.name = this.value;
10✔
824
        this.next();
10✔
825
      } else {
NEW
826
        this.unexpected();
×
827
      }
828
      if (this.eat(tokTypes._extends)) {
10!
NEW
829
        node.constraint = this._parseTSType();
×
830
      }
831
      if (this.eat(tokTypes.eq)) {
10!
NEW
832
        node.default = this._parseTSType();
×
833
      }
834
      return this.finishNode(node, 'TSTypeParameter');
10✔
835
    }
836

837
    parseMaybeTSTypeParameterDeclaration() {
838
      if (this._isStartOfTypeParameters()) {
151✔
839
        const node = this.startNode();
4✔
840
        const params = [];
4✔
841
        let first = true;
4✔
842
        this.next();
4✔
843
        while (!this.eat(tokTypes.relational)) {
4✔
844
          if (first) {
10✔
845
            first = false;
4✔
846
          } else {
847
            this.expect(tokTypes.comma);
6✔
848
          }
849
          if (this._isEndOfTypeParameters()) {
10!
NEW
850
            break;
×
851
          }
852
          params.push(this.parseTSTypeParameter());
10✔
853
        }
854
        node.params = params;
4✔
855
        return this.finishNode(node, 'TSTypeParameterDeclaration');
4✔
856
      }
857
    }
858

859
    parseTSTypeParameterInstantiation() {
860
      const node = this.startNode();
64✔
861
      const params = [];
64✔
862
      this.next(); // <
64✔
863
      let first = true;
64✔
864
      while ((this.value && !this._isEndOfTypeParameters()) || this.type === tokTypes.comma) {
64✔
865
        if (first) {
85✔
866
          first = false;
62✔
867
        } else {
868
          this.expect(tokTypes.comma);
23✔
869
        }
870

871
        params.push(this._parseTSType());
85✔
872
      }
873
      if (this._isEndOfTypeParameters()) {
62✔
874
        if (this.value.length > 1) {
58✔
875
          this.value = this.value.slice(1); // Fix to allow chaining of type parameters
1✔
876
        } else {
877
          this.next(); // >
57✔
878
        }
879
      }
880
      node.params = params;
62✔
881
      return this.finishNode(node, 'TSTypeParameterInstantiation');
62✔
882
    }
883

884
    parseMaybeTSTypeParameterInstantiation() {
885
      if (this._isStartOfTypeParameters()) {
×
NEW
886
        return this.parseTSTypeParameterInstantiation();
×
887
      }
888
    }
889

890
    _parseObjectLikeType(kind: string, prop: string) {
NEW
891
      const node = this.startNode();
×
NEW
892
      this.expect(tokTypes.braceL);
×
NEW
893
      const list = [];
×
894
      while (!this.eat(tokTypes.braceR)) {
×
895
        switch (this.type) {
×
896
          case tokTypes.name:
NEW
897
            const key = this.parseIdent();
×
898
            switch (this.type) {
×
899
              case tokTypes.parenL:
900
              case tokTypes.relational:
NEW
901
                list.push(this.parseTSMethodSignature(key));
×
NEW
902
                break;
×
903
              case tokTypes.colon:
904
              case tokTypes.semi:
905
              case tokTypes.comma:
906
              case tokTypes.braceR:
907
              case tokTypes.question:
NEW
908
                list.push(this.parseTSPropertySignature(key));
×
NEW
909
                break;
×
910
              default:
911
                if (this._hasPrecedingLineBreak()) {
×
NEW
912
                  list.push(this.parseTSPropertySignature(key));
×
NEW
913
                  continue;
×
914
                }
NEW
915
                this.unexpected();
×
916
            }
NEW
917
            break;
×
918
          case tokTypes.bracketL:
NEW
919
            const recover = this.tsPreparePreview();
×
NEW
920
            this.nextToken();
×
921
            if (this.type === tokTypes.name) {
×
NEW
922
              this.nextToken();
×
923
              switch (this.type) {
×
924
                case tokTypes.colon:
NEW
925
                  recover();
×
NEW
926
                  list.push(this.parseTSIndexSignature());
×
NEW
927
                  break;
×
928
                case tokTypes._in:
929
                  if (list.length === 0) {
×
NEW
930
                    recover();
×
NEW
931
                    return this.parseTSMappedType();
×
932
                  } else {
NEW
933
                    recover();
×
NEW
934
                    list.push(this.parseTSPropertySignature(null, true));
×
935
                  }
NEW
936
                  break;
×
937
                default:
NEW
938
                  recover();
×
NEW
939
                  list.push(this.parseTSPropertySignature(null, true));
×
940
              }
941
            } else {
NEW
942
              recover();
×
NEW
943
              list.push(this.parseTSPropertySignature(null, true));
×
944
            }
NEW
945
            break;
×
946
          case tokTypes._new:
NEW
947
            list.push(this.parseTSConstructSignatureDeclaration());
×
NEW
948
            break;
×
949
          default:
NEW
950
            this.unexpected();
×
951
        }
952
      }
NEW
953
      node[prop] = list;
×
NEW
954
      return this.finishNode(node, kind);
×
955
    }
956

957
    parseTSMethodSignature(key: any) {
NEW
958
      const node = this.startNodeAtNode(key);
×
NEW
959
      node.key = key;
×
960
      if (this.eat(tokTypes.question)) {
×
NEW
961
        node.optional = true;
×
962
      }
NEW
963
      node.typeParameters = this.parseMaybeTSTypeParameterDeclaration();
×
NEW
964
      this.expect(tokTypes.parenL);
×
NEW
965
      node.parameters = this.parseBindingList(
×
966
        tokTypes.parenR,
967
        false,
968
        this.options.ecmaVersion >= 8,
969
      );
970
      if (this.type === tokTypes.colon) {
×
NEW
971
        node.typeAnnotation = this.parseTSTypeAnnotation(true);
×
972
      }
NEW
973
      if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi);
×
NEW
974
      return this.finishNode(node, 'TSMethodSignature');
×
975
    }
976

977
    parseTSPropertySignature(key: any, computed = false) {
×
978
      let node;
979
      if (computed) {
×
NEW
980
        node = this.startNode();
×
NEW
981
        this.expect(tokTypes.bracketL);
×
NEW
982
        node.key = this.parseExpression();
×
NEW
983
        this.expect(tokTypes.bracketR);
×
984
      } else {
NEW
985
        node = this.startNodeAtNode(key);
×
NEW
986
        node.key = key;
×
987
      }
NEW
988
      node.computed = computed;
×
989
      if (this.eat(tokTypes.question)) {
×
NEW
990
        node.optional = true;
×
991
      }
992
      if (this.type === tokTypes.colon) {
×
NEW
993
        node.typeAnnotation = this.parseTSTypeAnnotation(true);
×
994
      }
NEW
995
      if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi);
×
NEW
996
      return this.finishNode(node, 'TSPropertySignature');
×
997
    }
998

999
    parseTSIndexSignature() {
NEW
1000
      const node = this.startNode();
×
NEW
1001
      this.expect(tokTypes.bracketL);
×
NEW
1002
      const index = this.parseIdent();
×
NEW
1003
      index.typeAnnotation = this.parseTSTypeAnnotation(true);
×
NEW
1004
      index.end = index.typeAnnotation.end;
×
1005
      if (this.options.locations) {
×
NEW
1006
        index.loc.end = index.typeAnnotation.loc.end;
×
1007
      }
NEW
1008
      node.index = index;
×
NEW
1009
      this.expect(tokTypes.bracketR);
×
NEW
1010
      node.typeAnnotation = this.parseTSTypeAnnotation(true);
×
NEW
1011
      if (!this.eat(tokTypes.comma)) this.eat(tokTypes.semi);
×
NEW
1012
      return this.finishNode(node, 'TSIndexSignature');
×
1013
    }
1014

1015
    parseTSMappedType() {
NEW
1016
      const node = this.startNodeAt(this.lastTokStart, this.lastTokStartLoc);
×
NEW
1017
      this.expect(tokTypes.bracketL);
×
NEW
1018
      node.typeParameter = this._parseTSTypeParameterInTSMappedType();
×
NEW
1019
      this.expect(tokTypes.bracketR);
×
1020
      if (this.eat(tokTypes.question)) {
×
NEW
1021
        node.optional = true;
×
1022
      }
1023
      if (this.type === tokTypes.colon) {
×
NEW
1024
        node.typeAnnotation = this.parseTSTypeAnnotation(true);
×
1025
      }
NEW
1026
      this.semicolon();
×
NEW
1027
      this.expect(tokTypes.braceR);
×
NEW
1028
      return this.finishNode(node, 'TSMappedType');
×
1029
    }
1030

1031
    _parseTSTypeParameterInTSMappedType() {
NEW
1032
      const node = this.startNode();
×
1033
      if (this.type === tokTypes.name) {
×
NEW
1034
        node.name = this.value;
×
NEW
1035
        this.next();
×
1036
      } else {
NEW
1037
        this.unexpected();
×
1038
      }
NEW
1039
      this.expect(tokTypes._in);
×
NEW
1040
      node.constraint = this._parseNonConditionalType();
×
NEW
1041
      return this.finishNode(node, 'TSTypeParameter');
×
1042
    }
1043

1044
    _parseMaybeTSExpression(node: any) {
1045
      if (
1,437!
1046
        this.type === tokTypes.prefix &&
1,437!
1047
        tsExprMarkup[this.value as keyof typeof tsExprMarkup] === tsExprMarkup['!']
1048
      ) {
NEW
1049
        node = this.parseTSNonNullExpression(node);
×
1050
      }
1051
      if (
1,437✔
1052
        this.type === tokTypes.name &&
1,496✔
1053
        tsExprMarkup[this.value as keyof typeof tsExprMarkup] === tsExprMarkup.as
1054
      ) {
1055
        node = this.parseTSAsExpression(node);
9✔
1056
      }
1057
      return node;
1,437✔
1058
    }
1059

1060
    parseTSAsExpression(expression: any) {
1061
      let node = expression;
9✔
1062
      while (
9✔
1063
        this.type === tokTypes.name &&
18✔
1064
        tsExprMarkup[this.value as keyof typeof tsExprMarkup] === tsExprMarkup.as
1065
      ) {
1066
        const _node = this.startNodeAtNode(node);
9✔
1067
        this.next();
9✔
1068
        _node.expression = node;
9✔
1069
        this._parseTSTypeAnnotation(_node);
9✔
1070
        node = this.finishNode(_node, 'TSAsExpression');
9✔
1071
      }
1072
      return expression;
9✔
1073
    }
1074

1075
    parseTSNonNullExpression(expression: any) {
NEW
1076
      let node = expression;
×
1077
      while (
×
1078
        this.type === tokTypes.prefix &&
×
1079
        tsExprMarkup[this.value as keyof typeof tsExprMarkup] === tsExprMarkup['!']
1080
      ) {
NEW
1081
        const _node = this.startNodeAtNode(node);
×
NEW
1082
        _node.expression = node;
×
NEW
1083
        this.next();
×
NEW
1084
        node = this.finishNode(_node, 'TSNonNullExpression');
×
1085
      }
NEW
1086
      return node;
×
1087
    }
1088
  };
1089
};
1090

1091
// acorn-class-fields plugin is needed, else parsing of some function types will not work
1092
// eslint-disable-next-line @typescript-eslint/no-require-imports
1093
const TypeParser = Parser.extend(tsPlugin as any, require('acorn-class-fields'));
65✔
1094

1095
export default TypeParser;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc