@@ -382,6 +382,12 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
382
382
* return _super.apply(this, tslib.__spread(arguments)) || this;
383
383
* ```
384
384
*
385
+ * or, since TypeScript 4.2 it would be
386
+ *
387
+ * ```
388
+ * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this;
389
+ * ```
390
+ *
385
391
* Such constructs can be still considered as synthetic delegate constructors as they are
386
392
* the product of a common TypeScript to ES5 synthetic constructor, just being downleveled
387
393
* to ES5 using `tsc`. See: https://github.com/angular/angular/issues/38453.
@@ -413,7 +419,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
413
419
* ```
414
420
* var _this = _super.apply(this, tslib.__spread(arguments)) || this;
415
421
* ```
416
- *
422
+ * or using the syntax emitted since TypeScript 4.2:
423
+ * ```
424
+ * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this;
425
+ * ```
417
426
*
418
427
* @param statement a statement that may be a synthesized super call
419
428
* @returns true if the statement looks like a synthesized super call
@@ -447,6 +456,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
447
456
* ```
448
457
* return _super.apply(this, tslib.__spread(arguments)) || this;
449
458
* ```
459
+ * or using the syntax emitted since TypeScript 4.2:
460
+ * ```
461
+ * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this;
462
+ * ```
450
463
*
451
464
* @param statement a statement that may be a synthesized super call
452
465
* @returns true if the statement looks like a synthesized super call
@@ -473,6 +486,10 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
473
486
* ```
474
487
* _super.apply(this, tslib.__spread(arguments)) || this;
475
488
* ```
489
+ * or using the syntax emitted since TypeScript 4.2:
490
+ * ```
491
+ * return _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this;
492
+ * ```
476
493
*
477
494
* @param expression an expression that may represent a default super call
478
495
* @returns true if the expression corresponds with the above form
@@ -500,7 +517,8 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
500
517
* This structure is generated by TypeScript when transforming ES2015 to ES5, see
501
518
* https://github.com/Microsoft/TypeScript/blob/v3.2.2/src/compiler/transformers/es2015.ts#L1148-L1163
502
519
*
503
- * Additionally, we also handle cases where `arguments` are wrapped by a TypeScript spread helper.
520
+ * Additionally, we also handle cases where `arguments` are wrapped by a TypeScript spread
521
+ * helper.
504
522
* This can happen if ES2015 class output contain auto-generated constructors due to class
505
523
* members. The ES2015 output will be using `super(...arguments)` to delegate to the superclass,
506
524
* but once downleveled to ES5, the spread operator will be persisted through a TypeScript spread
@@ -510,6 +528,12 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
510
528
* _super.apply(this, __spread(arguments)) || this;
511
529
* ```
512
530
*
531
+ * or, since TypeScript 4.2 it would be
532
+ *
533
+ * ```
534
+ * _super.apply(this, tslib.__spreadArray([], tslib.__read(arguments))) || this;
535
+ * ```
536
+ *
513
537
* More details can be found in: https://github.com/angular/angular/issues/38453.
514
538
*
515
539
* @param expression an expression that may represent a default super call
@@ -538,32 +562,79 @@ export class Esm5ReflectionHost extends Esm2015ReflectionHost {
538
562
// The other scenario we intend to detect: The `arguments` variable might be wrapped with the
539
563
// TypeScript spread helper (either through tslib or inlined). This can happen if an explicit
540
564
// delegate constructor uses `super(...arguments)` in ES2015 and is downleveled to ES5 using
541
- // `--downlevelIteration`. The output in such cases would not directly pass the function
542
- // `arguments` to the `super` call, but wrap it in a TS spread helper. The output would match
543
- // the following pattern: `super.apply(this, tslib.__spread(arguments))`. We check for such
544
- // constructs below, but perform the detection of the call expression definition as last as
545
- // that is the most expensive operation here.
546
- if ( ! ts . isCallExpression ( argumentsExpr ) || argumentsExpr . arguments . length !== 1 ||
547
- ! isArgumentsIdentifier ( argumentsExpr . arguments [ 0 ] ) ) {
565
+ // `--downlevelIteration`.
566
+ return this . isSpreadArgumentsExpression ( argumentsExpr ) ;
567
+ }
568
+
569
+ /**
570
+ * Determines if the provided expression is one of the following call expressions:
571
+ *
572
+ * 1. `__spread(arguments)`
573
+ * 2. `__spreadArray([], __read(arguments))`
574
+ *
575
+ * The tslib helpers may have been emitted inline as in the above example, or they may be read
576
+ * from a namespace import.
577
+ */
578
+ private isSpreadArgumentsExpression ( expression : ts . Expression ) : boolean {
579
+ const call = this . extractKnownHelperCall ( expression ) ;
580
+ if ( call === null ) {
548
581
return false ;
549
582
}
550
583
551
- const argumentsCallExpr = argumentsExpr . expression ;
552
- let argumentsCallDeclaration : Declaration | null = null ;
553
-
554
- // The `__spread` helper could be globally available, or accessed through a namespaced
555
- // import. Hence we support a property access here as long as it resolves to the actual
556
- // known TypeScript spread helper.
557
- if ( ts . isIdentifier ( argumentsCallExpr ) ) {
558
- argumentsCallDeclaration = this . getDeclarationOfIdentifier ( argumentsCallExpr ) ;
559
- } else if (
560
- ts . isPropertyAccessExpression ( argumentsCallExpr ) &&
561
- ts . isIdentifier ( argumentsCallExpr . name ) ) {
562
- argumentsCallDeclaration = this . getDeclarationOfIdentifier ( argumentsCallExpr . name ) ;
584
+ if ( call . helper === KnownDeclaration . TsHelperSpread ) {
585
+ // `__spread(arguments)`
586
+ return call . args . length === 1 && isArgumentsIdentifier ( call . args [ 0 ] ) ;
587
+ } else if ( call . helper === KnownDeclaration . TsHelperSpreadArray ) {
588
+ // `__spreadArray([], __read(arguments))`
589
+ if ( call . args . length !== 2 ) {
590
+ return false ;
591
+ }
592
+
593
+ const firstArg = call . args [ 0 ] ;
594
+ if ( ! ts . isArrayLiteralExpression ( firstArg ) || firstArg . elements . length !== 0 ) {
595
+ return false ;
596
+ }
597
+
598
+ const secondArg = this . extractKnownHelperCall ( call . args [ 1 ] ) ;
599
+ if ( secondArg === null || secondArg . helper !== KnownDeclaration . TsHelperRead ) {
600
+ return false ;
601
+ }
602
+
603
+ return secondArg . args . length === 1 && isArgumentsIdentifier ( secondArg . args [ 0 ] ) ;
604
+ } else {
605
+ return false ;
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Inspects the provided expression and determines if it corresponds with a known helper function
611
+ * as receiver expression.
612
+ */
613
+ private extractKnownHelperCall ( expression : ts . Expression ) :
614
+ { helper : KnownDeclaration , args : ts . NodeArray < ts . Expression > } | null {
615
+ if ( ! ts . isCallExpression ( expression ) ) {
616
+ return null ;
617
+ }
618
+
619
+ const receiverExpr = expression . expression ;
620
+
621
+ // The helper could be globally available, or accessed through a namespaced import. Hence we
622
+ // support a property access here as long as it resolves to the actual known TypeScript helper.
623
+ let receiver : Declaration | null = null ;
624
+ if ( ts . isIdentifier ( receiverExpr ) ) {
625
+ receiver = this . getDeclarationOfIdentifier ( receiverExpr ) ;
626
+ } else if ( ts . isPropertyAccessExpression ( receiverExpr ) && ts . isIdentifier ( receiverExpr . name ) ) {
627
+ receiver = this . getDeclarationOfIdentifier ( receiverExpr . name ) ;
628
+ }
629
+
630
+ if ( receiver === null || receiver . known === null ) {
631
+ return null ;
563
632
}
564
633
565
- return argumentsCallDeclaration !== null &&
566
- argumentsCallDeclaration . known === KnownDeclaration . TsHelperSpread ;
634
+ return {
635
+ helper : receiver . known ,
636
+ args : expression . arguments ,
637
+ } ;
567
638
}
568
639
}
569
640
0 commit comments