首页 > 解决方案 > 打字稿,对象的类型数组。只需要一个对象的属性设置为 true

问题描述

我想为对象数组创建一个 Typescript 类型。在这个对象数组中,我只需要一个对象的属性设置为 true。

您可能可以仅使用此打字稿示例来解决它,但我将提供一个冗长而详细的解释。

假设(只是一个例子!)我想重新创建一个<select>标签(或下拉列表)。

我的自定义选择有两个至少有两个选项,我总是希望只有一个对象处于活动状态。我有三个模型:

abstract class SimpleDropDownListElement {
  constructor(public label: string, public value: any) {}
}

class DropdownListElement extends SimpleDropDownListElement {
  constructor(public label: string, public value: any, public active?: false) {
    super(label, value);
  }
}

class DropdownActiveListElement extends SimpleDropDownListElement {
  active = true;
  constructor(public label: string, public value: any) {
    super(label, value);
  }
}

我想要一个至少有一个(或多个)DropdownListElement(s)和一个(总是一个 - 例如从不 0 或 2+)的数组DropdownActiveListElement。任何顺序(将 active 设置为 true 的对象可以在数组中的任何位置)。所以我的想法是创建一个这样的类型:

type DropDownOptionsArray = [DropdownActiveListElement, DropdownListElement, 
...Array<DropdownListElement>];

这可行,但是,我需要将具有 active 属性的对象设置为 true 作为我的数组的第一个元素。

所以我的想法是反转数组(不是很聪明),但如果数组包含超过 3 个值,我仍然会遇到问题。

type Reverse<Tuple> = Tuple extends [infer A, ...infer B]? [...Reverse<B>, A] : [];

const dropInvertedWithInvertedType: Reverse<DropDownOptionsArray> = 
[new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];


const dropInvertedWithInvertedType1: Reverse<DropDownOptionsArray> =
[new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b')]; // errors

然后我开始对休息元素发疯(希望TS v4能帮助我一些魔法):

type DropDownOptionsArray = [...[DropdownActiveListElement], 
...Array<DropdownListElement>, ...Array<DropdownListElement>];


// OK
const twoEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b')];
const fourEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b'), new DropdownListElement('b', 'b'),
new DropdownListElement('b', 'b')];

// should not error - but errors
const twoEntriesRandomPos: DropDownOptionsArray = [new DropdownListElement('b', 'b'),
new DropdownActiveListElement('a', 'a'), new DropdownListElement('b', 'b')];
const twoEntriesRandomPos: DropDownOptionsArray = [new DropdownListElement('b', 'b'),
new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];

// should error
const twoActiveEntries: DropDownOptionsArray = [new DropdownActiveListElement('a', 'a'),
new DropdownListElement('b', 'b'), new DropdownActiveListElement('a', 'a')];
const noActiveEntry : DropDownOptionsArray = [new DropdownListElement('b', 'b')]; // should have a different error

冗长地编写重载是不可行的,我们可以有一个包含 20 多个元素的数组。

总而言之,我需要这种类型:

谢谢!!

标签: javascripttypescript

解决方案


我相信这是不可能的,我已经创建了一些例子来验证这个断言。

作为解释,您确实可以在开始时定义具有多个固定类型的可变长度元组类型,例如

type A = number;
type B = string;
type C = null;

type X = [B, ...Array<A>];
const x:X = ["", 1, 2, 3];
const x[0] = ""; 

X 类型的变量必须以字符串开头,后跟 0 个或多个数字,这在分配整个数组时会正确检查类型。此外,分配给 x[0] 将作为字符串进行类型检查。

您还可以在最后定义一种或多种固定类型,例如

type X = [B, ...Array<A>, C];
const x:X = ["", 1, 2, 3, null];
x[0] = "";
x[4] = 4;

这一次,当分配给整个数组时,仍然执行类型检查。但是,它不能对最后一个元素的赋值进行类型检查。它可以做的最好的事情是检查第一个元素之后的元素是否为 A|C。这是因为 typescript 只能进行编译时检查,而不是运行时检查。

最后,类型定义中开头和结尾的显式类型之间的任何内容都简单地转换为所有类型的联合,例如

type X = [B, B, ...Array<A>, B, ...Array<A>, C, C];

被同等对待

type X = [B, B, ...Array<A|B>, C, C];

可以通过在编辑器中将鼠标悬停在 X 上来查看。

因此,您不能定义一个只有 1 个特定类型值的数组,并且如果可以的话,无论如何也无法在编译时对其进行监管。

如果可以实现,肯定会有问题。在更新活动元素时,您是否不会暂时拥有一个由 0 或 2 个活动元素组成的数组,即无效,因为更新不能以原子方式执行?


推荐阅读