angular 在使用*ngFor迭代数组的数组的字符串字面量的联合时出现编译错误,

4xrmg8kj  于 6个月前  发布在  Angular
关注(0)|答案(7)|浏览(57)

哪个@angular/*包是bug的来源?

core

这是个回归吗?

描述

在使用*ngFor指令时,无法在不出现编译错误的情况下遍历类型为数组的数组的字符串字面量的联合变量,尽管它是有效的TypeScript。

请提供一个链接,以便最小化地重现bug

https://stackblitz.com/edit/angular-7hhamp?file=src/main.ts

请提供您看到的异常或错误

Error in src/main.ts (16:36)
Type 'FooBar' is not assignable to type 'NgIterable<Foo[]>'.

请提供您发现此bug的环境(运行ng version)

Angular CLI: 12.2.18
Node: 16.19.0 (Unsupported)
Package Manager: npm 8.19.3
OS: linux x64

Angular: 12.2.17
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1202.18
@angular-devkit/build-angular   12.2.18
@angular-devkit/core            12.2.18
@angular-devkit/schematics      12.2.18
@angular/cdk                    12.2.13
@angular/cli                    12.2.18
@angular/material               12.2.13
@schematics/angular             12.2.18
rxjs                            6.6.7
typescript

还有其他信息吗?

  • 无响应*
hec6srdp

hec6srdp1#

你收到这个错误是因为你的类型 FooBar 与 Angular 的 *ngFor 指令所期望的 NgIterable<Foo[]> 类型不兼容。根据 Angular 文档(https://angular.io/api/core/NgIterable),NgIterable 被定义为 NgIterable = Array | Iterable;。
将第24行改为这样似乎可以解决问题。
values: NgIterable<FooBar> = [['a'], ['e'], ['c']] 更改为 type FooBar = Foo[] | Bar[];

e5njpo68

e5njpo682#

你收到这个错误是因为你的类型 FooBar 与 Angular 的 *ngFor 指令所期望的 NgIterable<Foo[]> 类型不兼容。根据 Angular 文档,NgIterable 被定义为类型 NgIterable = Array | Iterable;。

你弄错了。根据以下来自 Angular 文档的类型定义:

type NgIterable<T> = Array<T> | Iterable<T>;

那么类型 Foo[][](等价于 Array<Foo[]>)满足 NgIterable<Foo[]>。以下示例在使用 TypeScript 编译器时可以正常编译,证明了这一点:

type NgIterable<T> = Array<T> | Iterable<T>;
type Foo = 'foo';
type Bar = 'bar';
type FooBar = Foo[][] | Bar[][];

function f(x: NgIterable<Foo[]>): void {
    console.log(x);
}

const values: FooBar = [['foo']];

f(values);

将第24行更改为以下内容似乎可以解决问题。values: NgIterable<FooBar> = [['a'], ['e'], ['c']]
对于我的用例,这个值不应该有效。
并将 type FooBar = Foo[][] | Bar[][] 更改为 type FooBar = Foo[] | Bar[];
我不能简单地将二维数组更改为一维数组。我的用例需要二维数组。

ukqbszuj

ukqbszuj3#

这个问题似乎与任何联合类型有关,最容易复现的例子可以在这里找到:https://stackblitz.com/edit/angular-qndby2?file=src%2Fmain.ts
灵感来源于这个stackoverflow问题:https://stackoverflow.com/questions/75662030/why-doesnt-ngforof-directive-in-angular-work-with-union-types
看起来任何联合类型都某种程度上被忽略了,出于某种原因只取第一个类型。
例如,这个例子:

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `
<h1>Hello from {{name}}!</h1>
<a target="_blank" href="https://angular.io/start">
Learn more about Angular 
</a>
<div *ngFor="let element of array">
{{element}}
</div>
`,
})
export class App {
  name = 'Angular';

  array: string[] | number[] = ['1', '2'];
}

bootstrapApplication(App);

会产生以下错误:Type 'string[] | number[]' is not assignable to type 'NgIterable<string>'.
而这个例子(交换string[]和number[]):

import 'zone.js/dist/zone';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'my-app',
  standalone: true,
  imports: [CommonModule],
  template: `
<h1>Hello from {{name}}!</h1>
<a target="_blank" href="https://angular.io/start">
Learn more about Angular 
</a>
<div *ngFor="let element of array">
{{element}}
</div>
`,
})
export class App {
  name = 'Angular';

  array: number[] | string[] = ['1', '2'];
}

bootstrapApplication(App);

会产生以下错误:Type 'number[] | string[]' is not assignable to type 'NgIterable<number>'.

gojuced7

gojuced74#

你好,@briosheje,你是对的。我也遇到了相同的错误,我在几个情况下进行了测试,得到了相同的结果。
使用数组可能是解决遇到相同错误的人的一种方法。

export class App {
  name = 'Angular';

-  array: number[] | string[] = ['1', '2'];
+  array: Array<number | string> = ['1', '2'];
}

然后它就可以工作了

<div *ngFor="let element of array">
  {{element}}
</div>
zf9nrax1

zf9nrax15#

你好,@briosheje,你是对的。我也遇到了相同的错误,我在几个情况下进行了测试,得到了相同的结果。

使用数组作为替代方案可能对遇到相同错误的人有帮助。

export class App {
  name = 'Angular';

-  array: number[] | string[] = ['1', '2'];
+  array: Array<number | string> = ['1', '2'];
}

然后它就可以工作了。

<div *ngFor="let element of array">
  {{element}}
</div>

这不是一个可接受的解决方法,因为它们不是同一种类型。你不希望数组中可能同时包含字符串和数字。

/**
 * @param x - An array containing either a mix of strings and numbers, only strings or only numbers.
 */
function a(x: (string | number)[]): void {
  return;
}

/**
 * @param x - Either an array containing only strings, or an array containing only numbers.
 */
function b(x: string[] | number[]): void {
  return;
}

// Valid examples
a(['a', 1]);
a(['a', 'b']);
a([1, 1]);
b(['a', 'b']);
b([1, 2]);

// Invalid example
b(['a', 1]);
kxkpmulp

kxkpmulp6#

是的,你是对的,这不是同一件事。如果你编写了代码并且已经知道你会使用数组作为string[]或number[],那么你只能用它来避免错误。但是如果其他人要使用这段代码,你就是在允许他们以你不希望的方式发送数据。当然,这不是一个正确的解决方案,但这只是一个可以防止你在某些情况下出现错误的权宜之计。

w3nuxt5m

w3nuxt5m7#

@zilia-gmethot@demirmusa
有一个解决方案,而不是一个解决方法:**使用用户自定义类型保护。**我有一个灵活的API端点,返回 User[] | string[]

export function isUser(user: User | string | null): user is User {
  return user !== null && !!(user as User)?.id;
}

export function isUserArray(users: User[] | string[]): users is User[] {
  const [ user ] = users;
  return isUser(user);
}
<ng-container *ngIf='isUserArray(user.coaches)'>
// loop here
</ng-container>

相关问题