如何正确使用Angular自定义指令 解决TS2322类型报错
问题根源
这个报错是Angular结构型指令*语法糖的转换规则导致的:
- 你写的
*allowedUIAccess="'ADMIN'"是结构型指令的简写,Angular编译时会自动把它展开成<ng-template [allowedUIAccess]="'ADMIN'">包裹宿主li元素的形式 - 你单独写在li标签上的
[permissionSet]绑定,不会自动归属到allowedUIAccess指令上,而是被Angular识别为原生li元素的属性——但原生li不存在permissionSet属性,严格模板检查下Angular会把绑定值直接当字符串字面量处理,才会出现「字符串类型不能赋值给string[] | undefined」的类型错误。
另外你当前的指令代码还有个隐藏逻辑问题:@Input() set allowedUIAccess的执行时机不保证晚于permissionSet输入的赋值,直接在这个setter里拿this.permissionSet大概率会拿到undefined,没法做正确的权限判断。
修复方案
两种方案选一种即可,推荐第一种写法更简洁:
方案1:将permissionSet写入结构指令的微语法
结构指令的*简写支持用分号分隔额外的输入绑定,直接把permissionSet写在*allowedUIAccess表达式里即可,不需要单独在li上写绑定:
<li [routerLink]="['/admin']" *allowedUIAccess="'ADMIN'; permissionSet: (userprofile$ | async)?.permissions"> Admin </li>
你现有指令的输入定义不需要修改,只要保证输入属性名permissionSet和微语法里的key一致就能正确接收到值。
方案2:手动写不使用语法糖
如果觉得微语法不好理解,可以手动写Angular自动展开的完整模板结构,把两个输入都绑定到ng-template上的指令,完全避开语法糖的隐式转换问题:
<ng-template [allowedUIAccess]="'ADMIN'" [permissionSet]="(userprofile$ | async)?.permissions"> <li [routerLink]="['/admin']"> Admin </li> </ng-template>
额外逻辑优化建议
你需要等两个输入值都就绪后再做权限判断,不要直接在allowedUIAccess的setter里读permissionSet,可以在ngOnChanges生命周期里监听两个输入的变化,判断权限后再操作视图容器:
@Directive({ selector: '[allowedUIAccess]' }) export class AllowedUIAccessDirective implements OnChanges { @Input() permissionSet!: string[] | undefined; @Input() allowedUIAccess!: string; constructor( private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef, private uiService: UiService ) {} ngOnChanges(): void { // 两个输入都存在时再做权限校验 if (this.allowedUIAccess && this.permissionSet) { const hasPermission = this.permissionSet.includes(this.allowedUIAccess); this.viewContainer.clear(); if (hasPermission) { this.viewContainer.createEmbeddedView(this.templateRef); } } } }
注意:结构型指令不需要注入ElementRef,你操作的是TemplateRef对应的嵌入视图,不是当前宿主元素。
内容的提问来源于stack exchange,提问作者Benezir
相关产品推荐
相关产品推荐

