angular
angular copied to clipboard
Pipe `keyvalue` does not allow objects with optional keys.
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
An error occurs in HTML when using an interface (object) with option keys.
app.component.ts
import { Component } from '@angular/core';
export interface MyInterface {
one?: string;
two?: string;
three?: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
myData: MyInterface = {
one: 'One',
two: 'Two',
};
}
app.component.html
<ng-container *ngFor="let entry of myData | keyvalue">
<div>{{ entry.key }}: {{ entry.value }}</div>
</ng-container>
VSCode with Angular Language Service
No error when using serve
but on build
(prod). The VSCode extension Angular Language Service also shows this error in HTML. Never seen before. So I'm not 100% if this is a regression. But maybe I never worked with interface with an optional key here. Anyway, the code works during runtime. It seems to be a type interference. We need to allow optional keys (undefined).
Please provide a link to a minimal reproduction of the bug
https://github.com/infacto/issue-angular-keyvalue-optional/tree/main
Please provide the exception or error you saw
No overload matches this call.
The last overload gave the following error.
Argument of type 'MyInterface' is not assignable to parameter of type 'Record<keyof MyInterface, string> | ReadonlyMap<keyof MyInterface, string>'.ngtsc(2769)
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 13.3.8
Node: 14.19.0
Package Manager: npm 6.14.16
OS: win32 x64
Angular: 13.3.11
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1202.15
@angular-devkit/build-angular 13.3.8
@angular-devkit/core 12.2.15
@angular-devkit/schematics 12.2.15
@angular/cli 13.3.8
@schematics/angular 12.2.15
rxjs 6.6.7
typescript 4.6.4
Anything else?
I tested this in the latest Angular 12, 13 and 14. I see no error on StackBlitz (no Language Service ext.) and the code works well during runtime.
The error disappears when allow additional keys like this:
export interface MyInterface {
one?: string;
two?: string;
three?: string;
[key: string]: string;
}
Or casting to any. But these is just ugly and against clean or professional code.
Hello !
Having the same issue on Angular 14.2.
Here is a minimalist reproduction of the issue βΊοΈ
Reproduction of the bug
Template (not changed during reproduction steps)
<ng-container *ngFor="let metadata of keypipeIssue.metadata | keyvalue">
{{metadata.key}}: {{metadata.value}}
</ng-container>
Component (not changed during reproduction steps)
keypipeIssue: KeyPipeIssueTestObject = {
metadata: {
thing: "test"
}
}
Case 1: interface, no optionals
interface KeyPipeIssueTestObjectMetadata {
thing: string
}
interface KeyPipeIssueTestObject {
metadata: KeyPipeIssueTestObjectMetadata
}
--> βοΈ working
Case 2: interface, optionals
interface KeyPipeIssueTestObjectMetadata {
thing?: string
}
interface KeyPipeIssueTestObject {
metadata: KeyPipeIssueTestObjectMetadata
}
--> β template error
No overload matches this call.
The last overload gave the following error.
Argument of type 'KeyPipeIssueTestObjectMetadata' is not assignable to parameter of type 'Record<"thing", string | undefined> | ReadonlyMap<"thing", string | undefined> | null | undefined'.ngtsc(2769)
Case 3: no interface, optionals
interface KeyPipeIssueTestObject {
metadata: {
thing?: string
}
}
--> βοΈ working ... ???
Case 4: no interface, no optionals
interface KeyPipeIssueTestObject {
metadata: {
thing: string
}
}
--> βοΈ working ... of course π
Case 5: interface, optionals with additional keys
interface KeyPipeIssueTestObjectMetadata {
thing?: string
[key: string]: string | undefined // or any
}
interface KeyPipeIssueTestObject {
metadata: KeyPipeIssueTestObjectMetadata
}
--> βοΈ working
Hope that helps !
*** AppComponent *** export class AppComponent { name = 'Angular';
myData: MyInterface = { one: 'myOne', two: 'mytwo', }; }
export interface MyInterface { one?: string; two?: string; three?: string; [key: string]: string; }
app.component.html
- {{ item.key }} ---> {{ item.value }}
***Result *** one ---> myOne two ---> mytwo
Pipe with Optionala parameters work fine.
@pkozlowski-opensource Hi there, I see that there is a PR opened for this issue and its approved, but the movement inside it went stale. Is there any update on this issue?
It still needs approvals from the Angular team !