feat(ivy): static evaluation of TypeScript's __spread
helper (#30492)
The usage of array spread syntax in source code may be downleveled to a call to TypeScript's `__spread` helper function from `tslib`, depending on the options `downlevelIteration` and `emitHelpers`. This proves problematic for ngcc when it is processing ES5 formats, as the static evaluator won't be able to interpret those calls. A custom foreign function resolver is not sufficient in this case, as `tslib` may be emitted into the library code itself. In that case, a helper function can be resolved to an actual function with body, such that it won't be considered as foreign function. Instead, a reflection host can now indicate that the definition of a function corresponds with a certain TypeScript helper, such that it becomes statically evaluable in ngtsc. Resolves #30299 PR Close #30492
This commit is contained in:
@ -254,27 +254,44 @@ export interface CtorParameter {
|
||||
* itself. In ES5 code this can be more complicated, as the default values for parameters may
|
||||
* be extracted from certain body statements.
|
||||
*/
|
||||
export interface FunctionDefinition<T extends ts.MethodDeclaration|ts.FunctionDeclaration|
|
||||
ts.FunctionExpression> {
|
||||
export interface FunctionDefinition {
|
||||
/**
|
||||
* A reference to the node which declares the function.
|
||||
*/
|
||||
node: T;
|
||||
node: ts.MethodDeclaration|ts.FunctionDeclaration|ts.FunctionExpression|ts.VariableDeclaration;
|
||||
|
||||
/**
|
||||
* Statements of the function body, if a body is present, or null if no body is present.
|
||||
* Statements of the function body, if a body is present, or null if no body is present or the
|
||||
* function is identified to represent a tslib helper function, in which case `helper` will
|
||||
* indicate which helper this function represents.
|
||||
*
|
||||
* This list may have been filtered to exclude statements which perform parameter default value
|
||||
* initialization.
|
||||
*/
|
||||
body: ts.Statement[]|null;
|
||||
|
||||
/**
|
||||
* The type of tslib helper function, if the function is determined to represent a tslib helper
|
||||
* function. Otherwise, this will be null.
|
||||
*/
|
||||
helper: TsHelperFn|null;
|
||||
|
||||
/**
|
||||
* Metadata regarding the function's parameters, including possible default value expressions.
|
||||
*/
|
||||
parameters: Parameter[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Possible functions from TypeScript's helper library.
|
||||
*/
|
||||
export enum TsHelperFn {
|
||||
/**
|
||||
* Indicates the `__spread` function.
|
||||
*/
|
||||
Spread,
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter to a function or method.
|
||||
*/
|
||||
@ -404,8 +421,7 @@ export interface ReflectionHost {
|
||||
*
|
||||
* @returns a `FunctionDefinition` giving metadata about the function definition.
|
||||
*/
|
||||
getDefinitionOfFunction<T extends ts.MethodDeclaration|ts.FunctionDeclaration|
|
||||
ts.FunctionExpression>(fn: T): FunctionDefinition<T>;
|
||||
getDefinitionOfFunction(fn: ts.Node): FunctionDefinition|null;
|
||||
|
||||
/**
|
||||
* Determine if an identifier was imported from another module and return `Import` metadata
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost} from './host';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost, TsHelperFn} from './host';
|
||||
import {typeToValue} from './type_to_value';
|
||||
|
||||
/**
|
||||
@ -125,11 +125,15 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
return this.getDeclarationOfSymbol(symbol);
|
||||
}
|
||||
|
||||
getDefinitionOfFunction<T extends ts.FunctionDeclaration|ts.MethodDeclaration|
|
||||
ts.FunctionExpression>(node: T): FunctionDefinition<T> {
|
||||
getDefinitionOfFunction(node: ts.Node): FunctionDefinition|null {
|
||||
if (!ts.isFunctionDeclaration(node) && !ts.isMethodDeclaration(node) &&
|
||||
!ts.isFunctionExpression(node)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
node,
|
||||
body: node.body !== undefined ? Array.from(node.body.statements) : null,
|
||||
helper: null,
|
||||
parameters: node.parameters.map(param => {
|
||||
const name = parameterName(param.name);
|
||||
const initializer = param.initializer || null;
|
||||
|
Reference in New Issue
Block a user