当 A 和 O 都是对象的属性时,使用字符串数组 A 的值作为对象 O 中的键

Posted

技术标签:

【中文标题】当 A 和 O 都是对象的属性时,使用字符串数组 A 的值作为对象 O 中的键【英文标题】:Use values of string array A as keys in object O when A and O are both properties of an object 【发布时间】:2021-11-22 16:51:45 【问题描述】:

我想创建一个对象类型,具有valuesvaluesMap 属性。 values 应该是一个字符串数组,valuesMap 应该是一个对象,其键是 values 中的条目,其值是字符串或布尔值。我正在努力在打字稿中定义这种类型约束。这是我尝试过的:

type FilterOptionBase<K extends string> = 
  name: string;
  values: K[];
  multiselect: boolean;
  isBoolean?: boolean;
  valuesMap?: 
    [key in K]: string | boolean;
  ;
;

type FilterOption = FilterOptionBase<string>

const myBadFilter: FilterOption = 
    name: 'Status',
    values: ["Online", "Offline"],
    multiselect: false,
    valuesMap: 
        Online: true,
        NotSure: false // <------ should error but doesnt
    

TypeScript playground

我觉得这应该相对简单,但解决方案却让我无法理解。如果以前有人问过这个问题,请把我带到正确的地方!

还有:

如果isBoolean 定义为true,我们是否也可以将valueMap 的值约束为布尔值,而不是boolean | string

【问题讨论】:

【参考方案1】:

您的示例不起作用的原因是:

type FilterOption = FilterOptionBase<string>

...将string 传递给FilterOptionBase,本质上是这样做的:

type FilterOptionBase = 
  name: string;
  values: string[];
  multiselect: boolean;
  isBoolean?: boolean;
  valuesMap?: 
    [key in string]: string | boolean;
  ;
;

因此,要正确输入,您需要在使用该类型时传入您期望的字符串列表,如下所示:

const myBadFilter: FilterOptionBase<"Online" | "Offline"> = 
  name: 'Status',
  values: ["Online", "Offline"],
  multiselect: false,
  valuesMap: 
      Online: true,
      NotSure: false // <------ errors now!
  

但是,我认为您实际上尝试做的是使用FilterOptionBase 类型作为各种不同对象的各种模板,不幸的是没有办法那样做。你不能有一个既从自身读取约束自身的类型。

但是您可以访问传递给函数的对象上的值,所以这是我过去使用过的一种解决方法,效果很好: p>

const filterFactory = <K extends string>(v: FilterOptionBase<K>) => v

const myBadFilter = filterFactory(
  name: 'Status',
  values: ['Online', 'Offline'],
  multiselect: false,
  valuesMap: 
    Online: true, 
    NotSure: false, // <------ it's an error!
  
)

“也”回答

是的,可以根据 isBooleantrue 来更改它。你只需要做一个联合:

type foo = 
  isBoolean: true
  value: boolean
 | 
  isBoolean: false
  value: string

虽然它确实变得有点棘手,因为你必须使用工厂函数来解决这个问题。这里是:

type FilterOptionBase<
  K extends string,
  B extends boolean | undefined,
> = B extends true
  ? 
      name: string
      values: K[]
      multiselect: boolean
      isBoolean: B
      valuesMap?: 
        [key in K]: boolean
      
    
  : 
      name: string
      values: K[]
      multiselect: boolean
      isBoolean?: B
      valuesMap?: 
        [key in K]: string
      
    

const filterFactory = <K extends string, B extends boolean | undefined>(
  v: FilterOptionBase<K, B>,
) => v

const myBadFilter = filterFactory(
  name: "Status",
  values: ["Online", "Offline"],
  multiselect: false,
  isBoolean: true,
  valuesMap: 
    Online: true,
    Offline: "foo", // <------ also an error!
  ,
)

const myOtherBadFilter = filterFactory(
  name: "Status",
  values: ["Online", "Offline"],
  multiselect: false,
  isBoolean: false,
  valuesMap: 
    Online: true, // <------ also an error!
    Offline: "foo",
  ,
)

【讨论】:

以上是关于当 A 和 O 都是对象的属性时,使用字符串数组 A 的值作为对象 O 中的键的主要内容,如果未能解决你的问题,请参考以下文章

当属性是字符串时,如何按属性从左到右排序对象数组,首先具有数字?

第一部分 JavaScript语言核心

在Swift中使用Realm进行过滤,给我一个对象数组,但是当打印一个属性时,它会给我一个空字符串

如何从数组中删除重复值?当数组内的对象属性未定义时,我的代码失败

AutoCompleteTextView

JS对象数组多条件排序