当试图使用双向绑定将数字传递给@Input x:string|number时,角度模板严格模式会引发错误



我想通过在tsconfig.json中设置"strictTemplates": true来更新我的应用程序,使其使用Angular的模板Strict模式。在使用新配置运行ng serve之后,我出现了一个奇怪的错误。

我有一个在很多地方使用的共享组件

@Component({
selector: 'my-shared-component',
...
})
export class SharedComponent {
@Input() selectedId: string | number;
@Output() selectedIdChange = new EventEmitter<string | number>()
@Input() disabled: boolean;
...
}

并且通过<my-shared-component disabled="true">会抛出一个错误,这是可以的。

当我试图将number传递到@Input() selectedId: string | number;时,问题就开始了,即:

export class OtherComponent {
myNumberId: number;
...
}
<my-shared-component [(selectedId)]="myNumberId">

然后抛出一个错误:

TS2322:类型"string|number"不可分配给类型"number">

我不想用string | number替换myNumberId的类型,因为它也用于另一个具有@Input id: number(并且必须是数字(的组件中。另外,myNumberId来自服务器,我知道这是一个数字,所以我认为如果我用string | number替换它,会导致设计不好。

我也不想使用strictAttributeTypes配置为false,因为之后<my-shared-component disabled="true">将不再抛出错误。

我想知道是否存在某种typescript实用程序类型(或类似的类型(,使@Input() selectedId: string | number只接受字符串或数字,而不强制父级的变量为string | number。或者我可以做其他事情来解决这个问题吗?

好吧,它正确地抛出了一个错误-输出EventEmitter将发出stringnumber,这将尝试为严格类型的数字赋值。

您可以尝试在OtherComponent:中分离setter和getter

_myNumberId: number;
get myNumberId(): number {
return this._myNumberId;
}
get myNumberId(value: string | number): number {
if (typeof value === 'string') {
// This is a stub, you might want to go with something else like parseFloat()
// or just throw.
this._myNumberId = parseInt(value, 10);
} else {
this._myNumberId = value;
}
}

这部分地保持了检查类型的number,如果值被提取,并且仍然允许从stringnumber设置值,因此它将删除编译错误。但这不是一个好的解决方案,因为它看起来像一种代码气味;松开";setter上的类型检查。

如果你退后一步,从更大的角度来看——也许你应该定义什么是";Id";在您的应用程序中,并在通用组件中使用该类型?正在进行的事情:

export type MyId = string | number; 

然后,您的组件可以使用泛型扩展该类型,这将进行严格的类型检查:

@Component({
selector: 'my-shared-component',
...
})
export class SharedComponent<T extends MyId> {
@Input() selectedId: T;
@Output() selectedIdChange = new EventEmitter<T>()
@Input() disabled: boolean;
...
}

这将允许在双向绑定中将stringnumber指定为输入/输出,并在两个位置将类型强制为其中的一个。稍后,如果您决定需要将额外的逻辑应用于Id本身,则可以在一个位置更改它(类型定义(,并查看它是否违反了任何类型检查。

当然,你总是可以不定义类型,并这样做:

export class SharedComponent<T extends string | number> {

但是,您可能还想在几个不同的地方强制执行这一点,因此自定义值类型似乎是一个不错的方法。

这里有一个使用泛型方法的stackblitz,它在双向绑定中正确地分配了stringnumber值,但未能分配不同的类型(Date(。如果删除日期分配,它将正确编译。

您可以尝试用string & number替换string | number

你也可以试着这样发送:

<my-shared-component [(selectedId)]="+myNumberId">

最新更新