Angular使用graphql-codegen报Object is of type unknown错误
问题根因
两个编译错误本质是GraphQL生成类型的可空性,与模板、分页管道要求的非空类型不匹配导致:
- 第一个错误:graphql-codegen生成的
BlogPostsQuery类型中,blogPost字段是可空联合类型Array<...>|null|undefined,且数组内部元素也允许为null,而paginate管道要求传入确定的非空集合类型Collection<unknown>,带null/undefined的联合类型无法直接赋值。 - 第二个错误:因为
blogPost数组元素可能为null,*ngFor遍历时TypeScript无法确定每个blog的具体类型,会将其推断为unknown,直接访问blog.image属性就会触发类型错误。另外原模板直接写blog.image.urls没有做空值判断,本身也存在运行时空指针风险。
修复方案
第一步:修改组件TS代码,提前处理空值输出明确类型
不要直接把整个Query对象传给模板处理,在组件逻辑层就过滤空值,输出类型明确的博客列表Observable,从根源解决类型不匹配问题。
修改后的组件代码:
import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; // 导入生成的类型,新增导入Maybe类型辅助 import { BlogPostsQuery, BlogPostsGQL } from '../graphql/graphql'; // 从生成的Query类型中直接推导单条博客的类型,无需重复手写 type BlogPost = Exclude<Exclude<BlogPostsQuery['blogPost'], null | undefined>[number], null | undefined>; @Component({ selector: 'app-blog', templateUrl: './blog.component.html', styleUrls: ['./blog.component.scss'] }) export class BlogComponent implements OnInit { public responsive: boolean = true; // 定义类型明确的博客列表流 blogList$!: Observable<BlogPost[]>; modalIsActive!: boolean; values!: string; public config = { itemsPerPage: 1, // 分页默认页码从1开始,原配置写0会导致分页组件异常 currentPage: parseInt(sessionStorage.getItem('blogPage') || '') || 1 }; constructor( private router: Router, private allBlogPostGQL: BlogPostsGQL ) {} ngOnInit() { this.blogList$ = this.allBlogPostGQL .watch() .valueChanges .pipe( map(res => { const rawList = res.data.blogPost; // 过滤所有空值,通过类型谓词保证返回纯BlogPost类型的数组 return (rawList || []).filter((blog): blog is BlogPost => !!blog); }) ); } onPageChange(number: number) { sessionStorage.setItem('blogPage', JSON.stringify(number)); this.config.currentPage = number; } showMore(name: string) { this.router.navigate(['/blog', name]); } }
第二步:修改模板代码,补全空判断、修正属性绑定
替换数据源为处理好的blogList$,补全可选链空判断,同时修正原逻辑中直接拼接数组到图片地址的错误:
<ng-container *ngFor="let blog of blogList$ | async | paginate: config"> <img class="image" *ngIf="blog.image?.urls?.length" [src]="'http://localhost:5002' + blog.image.urls[0]" alt="Placeholder image" > </ng-container>
注意点:
- 用Angular属性绑定
[src]代替字符串插值拼接地址,避免变更检测异常 - 生成类型中
urls是字符串数组类型,不能直接把整个数组拼接到地址后,按需取对应下标的元素即可,常规封面图取第一个元素 - 可选链
?.会自动做空值判断,避免运行时访问null属性报错
(可选)优化codegen配置减少冗余空类型
如果你的GraphQL接口实际返回的博客列表、列表元素本身不会为null,可以修改codegen.yml配置,让生成的类型默认不带多余的null定义,从根源减少空判断代码:
generates: src/app/graphql/graphql.ts: plugins: - typescript - typescript-operations - typescript-apollo-angular config: arrayNullability: false # 数组本身不默认添加null类型 nonArrayItemNullability: false # 数组元素不默认添加null类型
该配置必须和实际接口返回逻辑一致,不要为了消除编译错误随意修改,否则会引发运行时异常。
内容的提问来源于stack exchange,提问作者amin mohammadi
相关产品推荐
相关产品推荐

