angular/angular · error · Error

Unsupported property access for type reference: ${expression

Error message

Unsupported property access for type reference: ${expression.property.type}

What it means

Thrown by the Babel AST factory of the Angular partial-declaration linker when it converts a type/entity reference from partially compiled code into a static TypeScript qualified name. Walking a member expression such as `Ns.Comp` requires the property part (`.Comp`) to be an identifier; a computed access (`Ns['Comp']`, `Ns[k]`, `Ns?.Comp`) reaches this throw. The declaration metadata of the library being linked contains a type reference the linker cannot reduce to a static dotted name.

Source

Thrown at packages/compiler-cli/linker/babel/src/ast/babel_ast_factory.ts:386

      node.typeAnnotation = t.tsTypeAnnotation(type);
    }

    return node;
  }
}

function getEntityTypeFromExpression(
  expression: t.Expression | t.Super,
): t.Identifier | t.TSQualifiedName {
  if (t.isIdentifier(expression)) {
    return expression;
  }

  if (t.isMemberExpression(expression)) {
    const left = getEntityTypeFromExpression(expression.object);

    if (!t.isIdentifier(expression.property)) {
      throw new Error(
        `Unsupported property access for type reference: ${expression.property.type}`,
      );
    }

    return t.tsQualifiedName(left, expression.property);
  }

  throw new Error(`Unsupported expression for type reference: ${expression.type}`);
}

function isLExpression(expr: t.Expression): expr is Extract<t.LVal, t.Expression> {
  // Some LVal types are not expressions, which prevents us from using `t.isLVal()`
  // directly with `assert()`.
  return t.isLVal(expr);
}

View on GitHub (pinned to 51cb07e980)

Solutions

  1. Recompile the library with the Angular compiler so metadata contains direct references
  2. Stop post-emit transforms (minifiers/formatters/rewriters) that convert `a.b` into `a['b']` inside ɵɵngDeclare* calls
  3. Align the app's @angular/compiler-cli and CLI versions with (or newer than) the library's Angular version
  4. If the published library is unmodified, reproduce with a plain CLI build and report the issue to the library maintainer

Example fix

// before
ɵɵngDeclareDirective({ minVersion: '14.1', ngImport: core, type: exports['MyDir'], selector: '[myDir]' });
// after
ɵɵngDeclareDirective({ minVersion: '14.1', ngImport: core, type: MyDir, selector: '[myDir]' });
Defensive patterns

Strategy: type-guard

Validate before calling

import * as t from '@babel/types';
function isStaticTypeRef(e: t.Expression): boolean {
  return t.isIdentifier(e) ||
    (t.isMemberExpression(e) && !e.computed &&
      t.isIdentifier(e.property) && isStaticTypeRef(e.object as t.Expression));
}
// run over entity-valued fields (type, encapsulation, ...) before linking
const safe = t.isObjectExpression(typeArg) && typeArg.properties.every(
  (p) => !t.isObjectProperty(p) || !isEntityField(p) || isStaticTypeRef(p.value as t.Expression),
);

Type guard

const isStaticTypeReference = (e: t.Expression): boolean =>
  t.isIdentifier(e) ||
  (t.isMemberExpression(e) && !e.computed &&
    t.isIdentifier(e.property) && isStaticTypeReference(e.object as t.Expression));

Prevention

When it happens

Trigger: Linking ɵɵngDeclareDirective/Component/Injectable metadata whose entity slot (e.g. `type`) is a computed member expression like exports['MyDir']; FESM bundles where a minifier or rewriter turned dotted access into bracket access before linking; hand-maintained partial declaration calls.

Common situations: ng build or the standalone @angular/compiler-cli/linker consuming a partially compiled (compilationMode 'partial') library that was aggressively post-processed; version skew where a newer library's output meets an older linker; custom bundler pipelines that rewrite member expressions.

Related errors


AI-assisted analysis of angular/angular@51cb07e980 (2026-08-22). Data as JSON: /api/errors/1e5544dd94d9fba4. Report an issue: GitHub.