question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

Type check directive template context

See original GitHub issue

šŸš€ feature request

Relevant Package

@angular/compiler

Description

Template contexts appear not to be checked.

    <ng-container *ngIf="data(); let d">
      {{ d.a }}
      {{ d.c }}
    </ng-container>

There is zero checking for the type of d.

Example: https://github.com/pauldraper/angular-template-context I would like ng build to fail.

Describe the solution youā€™d like

Template context types are type checked.

Describe alternatives youā€™ve considered

Donā€™t use template contexts.

Iā€™ve also tried using Ivy but it seems broken.

ERROR in __ng_typecheck__.ts:5:22 - error TS2304: Cannot find name 'Partial'.

5 const _ctor1: (init: Partial<Pick<i1.NgIf, "ngIf" | "ngIfThen" | "ngIfElse">>) => i1.NgIf = (null!);

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:2
  • Comments:8 (6 by maintainers)

github_iconTop GitHub Comments

4reactions
alexzuzacommented, Jul 15, 2019

Angular Ivy type-checking system uses Partial and Pick built-in TypeScript types. They are declared in lib.es5.d.ts which means that you have to provide at least es5 in lib option of your tsconfig.json file:

{
  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "enableIvy": true
  },
  "compilerOptions": {
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "lib": ["dom", "es5"]
                     \/
                  add this               
  }
}

Once youā€™ve done, the compiler will be executed without any errors.

Why? Angular Ivy type-checking system for ngIf only supports ngIf guard but not template context guard. The generated code for your case looks like:

import * as i0 from './app.component';
import * as i1 from '@angular/common';

const _ctor1: (init: Partial<Pick<i1.NgIf, "ngIf" | "ngIfThen" | "ngIfElse">>) => i1.NgIf = (null!);

function _tcb1(ctx: i0.AppComponent) { if (true) {
    var _t1 = _ctor1({ ngIf: ctx.data() });
    var _t2: any = (null!);
    if (ctx.data()) {
        var _t3 = _t2.$implicit;
        var _t4 = document.createElement("ng-container");
        "" + _t3.a + _t3.c;
    }
} }

where the type of _t3 variable is any.

The NgIf directive doesnā€™t implement ngTemplateContextGuard feature like NgForOf does. What could work here is:

interface NgIfContext<T = any> {
  $implicit: T;
}

class GuardedNgIf<T> {
  ngIf: T;
  static ngTemplateContextGuard<T>(dir: GuardedNgIf<T>, ctx: any): ctx is NgIfContext<T> {
    return true;
  }
}
const _ctor1: <T = any>(init: Partial<Pick<GuardedNgIf<T>, "ngIf">>) => GuardedNgIf<T> = (null!);

function _tcb1(ctx: AppComponent) { 
  var _t1 = _ctor1({ ngIf: ctx.data() });
  var _t2: any = (null!);
  if (GuardedNgIf.ngTemplateContextGuard(_t1, _t2)) {
    var _t3 = _t2.$implicit;

    "" + _t3.a + _t3.c; // Error:(47, 22) TS2339: Property 'c' does not exist on type '{ a: string; b: string; }'.
  }
}

// cc @JoostK

1reaction
simeylacommented, Aug 10, 2019

Even without Ivy you can sort of achieve this today (8.1.2), if you use *ngFor instead of *ngIfā€¦

This is quite a clumsy hack - but works to achieve what the OP was trying to do:

export type Product = { sku: string, price: number };

// helper function to take a single product and convert it to the correct type 
// this works even for a `*ngTemplateOutletContext` parameter which is why I needed this
coerceProductArray(product: any): Product[]
{
    return [ product ];   // creates a single item array
}

Then iterate through this list - (of one item) - and the correct type for product will be available inside. The attempted multiplication of a string below will not be allowed!

<ng-container *ngFor="let product of coerceProductArray(product)">

    <!-- Compiles -->
    {{ product.sku }}
    {{ product.price }}
    {{ product.price * 2 }}

    <!-- These have squiggles! Yay -->
    {{ product.desc }}
    {{ product.price + 'cat' }}
    {{ product.sku * 2 }}

</ng-container>

What Iā€™m really having a hard time understanding is why ng_for_of.ts is generic, but ng_if.ts isnā€™t. Is it just not possible for *ngIf to use the same mechanism that *ngFor does to achieve this.

Whatā€™s the reason ng_if isnā€™t a generic type today? Where does Ivy help with this in future - will this problem just go away some day. (And yes - I too was very confused by the comments highlighted in the previous post).

I was about to embark on creating my own *ngIf but I figured it would be a futile exercise until i can understand better whatā€™s going on - this seemed to be the closest issue.

Thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

Template type checking - Angular
Infers template context types where configured (for example, allowing correct type-checking of NgFor ); Infers the correct type of $event in component/directive ......
Read more >
Angular structural directive context template type checking
There is no problem with the directive. The problem is that the angular language service is not updated yet to infer those types...
Read more >
Directive Type Checking - DEV Community ā€ ā€
The ngTemplateContextGuard returns true since this directive will always pass a context of type DemoContext to the template. Now when we useĀ ...
Read more >
Type-checking templates in Angular View Engine and Ivy
In this article we'll explore how Angular type-checks templates, ... keep the correct type of the context for the template that this directive...
Read more >
Typing the Context Object in Angular Structural Directives
Great, our context has the correct type. The way it works is that Angular will compile our template and create Type Check Blocks....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found