关于angular:在Typescript界面中具有可选项目的定长数组

fixed-length array with optional items in Typescript interface

Angular在其FormBuilder类中引入了模型驱动的表单,其主要方法group具有如下签名:

1
2
3
group(controlsConfig: {
        [key: string]: any;
    }): FormGroup;

any实际上是一个具有以下格式的数组:

1
2
3
4
5
[
    initial value of model's property,
    sync validator(s),
    async validator(s)
]

仅需要第一个元素的地方。

我决定要比该类型强一点的类型,特别是在与强类型模型相关的任何东西上,因此我用T重新定义该函数:

1
2
3
4
5
declare interface FormBuilder2 extends FormBuilder {
    group< T >(controlsConfig: {
        [K in keyof T]?: [T[K], ValidatorFn | ValidatorFn[] | null, ValidatorFn | ValidatorFn[] | null];
    }): FormGroup;
}

这也意味着我在HTML中的所有formControlNames(当然也在group()调用中)都必须与模型的属性匹配,这是我更喜欢的。

似乎有效,但要满足以下条件:

1
2
3
4
5
    this.optionsForm = this.formBuilder2.group<CustomerModel>({
        status:    [this.model.status, [Validators.required], null],
        lastOrder: [this.model.lastOrder, null, null],
        comments:  [this.model.comments, null, null],
    });

我必须在未使用的阵列插槽上提供null

有没有一种方法可以使Typescript忽略对多余的null的需要?


对于元组类型,没有真正的类型安全的方法,因为元组可以接受额外的元素。也就是说,例如,元组类型[A, B, C]实际上将接受类型为A | B | C的其他元素(请参阅docs)。

但是,有解决方案! (请参阅下面的尝试3)

(顺便说一句,您忽略了Angular为异步验证器提供了一个不同的接口:AsyncValidatorFn。)

尝试1:

1
[K in keyof T]?: [T[K] | ValidatorFn | ValidatorFn[] | null];

几乎不比any键入好(可能更糟,因为它看起来具有误导性意义)。

尝试2:

1
2
3
4
[K in keyof T]?:
  [T[K]] |
  [T[K], ValidatorFn | ValidatorFn[]] |
  [T[K], ValidatorFn | ValidatorFn[] | null, AsyncValidatorFn | AsyncValidatorFn[]];

乍一看似乎更好。但是问题是Typescript编译器只会在万不得已时抛出错误。因此它将接受以下内容:

1
someStringField: ['hi', 'hello']

因为这符合[T[K]](因为Typescript中的元组允许具有额外的元素)。

尝试3:

有一个更好的解决方案,令我惊讶的是。我在撰写此答案的过程中发现了这一点,同时在Typescript GitHub存储库上阅读了此问题。

1
2
3
4
5
[K in keyof T]?: {
  0: T[K];
  1?: ValidatorFn | ValidatorFn[];
  2?: AsyncValidatorFn | AsyncValidatorFn[];
};

这是对先前尝试的一种改进,因为前三个元素始终都经过正确的类型检查。 ['hi', 'hello']正确给出了一个编译错误。按照通常的结构化类型,允许其他元素,并且可以是任何其他元素,但这没关系。

希望这能解决您的问题。