类型上不存在该属性/无法用作索引类型。

3

我想要一个对象,它可以在其根目录下包含字符串,并在arrayValues对象中包含字符串数组。这是将在应用程序中使用的配置对象。

此对象及其键将是动态的(通过用户状态值定义),因此我无法根据特定键名称指定类型。

我将从应用程序的一部分向“arrayValues”添加数组,从另一部分向“filters”添加字符串。

然而,当访问对象属性时出现了错误:
TS PLAYGROUND

export type FiltersStateType = {
  filters:
    | {
        arrayValues: { // note: this should only appear once in filters object
          [key: string]: Array<string>
        }
      }
    | { [key: string]: string }
}

const example: FiltersStateType = {
  filters: {
    arrayValues: {
      sandwiches: ['ham', 'cheese'],
      milkshakes: ['choc', 'banana'],
    },
    randomString: 'hello',
  },
}

example.filters.arrayValues.sandwiches.filter((item: string) => item !== 'ham') 
// ❌ Property 'sandwiches' does not exist on type 'string | { [key: string]: string[]; }'.
// ❌ Property 'sandwiches' does not exist on type 'string'

const stringKey = 'milkshakes'
example.filters.arrayValues[stringKey].filter((item: string) => item !== 'choc') 
// ❌ Element implicitly has an 'any' type because expression of type '"milkshakes"' can't be used to index type 'string | { [key: string]: string[]; }'.
// ❌ Property 'milkshakes' does not exist on type 'string | { [key: string]: string[]; }'

example.filters.randomString = 'bye'
// ❌ Property 'randomString' does not exist on type '{ arrays: { [key: string]: string[]; }; } | { [key: string]: string; }'.
// ❌ Property 'randomString' does not exist on type '{ arrays: { [key: string]: string[]; }; }

我希望 TypeScript 能理解 FiltersStateType 中指定的对象类型,但它似乎不能。你知道这是为什么吗?


2
“我期望TypeScript能够理解FiltersStateType中指定的对象类型”,它确实做到了。但是,你的类型可能有也可能没有sandwiches。这就是为什么TS会阻止你,因为使用可能是正确的,也可能是不正确的。 - VLAZ
如果问题只是存在与否,我会期望可选链解决这个问题 - 但它并没有。你知道如何解决吗?谢谢! - Hombre Lobo
2个回答

2
这是因为被突出显示的Union引起的:
export type FiltersStateType = {
  filters:
    | {
        arrayValues: {
          [key: string]: Array<string>
        }
      }
    | { [key: string]: string } // this one
// ---^^^^^^^^^^^^^^^^^^^^^^^^^---
}

由于您在联合类型中指定了{ [key: string]: string },因此下面的对象是有效的:

const example: FiltersStateType = {
  filters: {
    arrayValues: 'hello',
  },
}

TypeScript无法强制实施arrayValues键的值的形状,因为{ [key: string]: string }本身意味着任何键都可以具有字符串值。理想情况下,您必须更改类型定义的结构,就像这样:

// this is just an example, you should
// restructure as per your requirements 
type FiltersStateType = {
  filters: { [key: string]: string },
  arrayValues: { [key: string]: Array<string> }
}

1
谢谢!非常有帮助!标记为答案! - Hombre Lobo

1
您可以这样声明您的FiltersStateType类型:

type FiltersStateType = {
  filters: {
    [key: string]: string | {[key: string]: string[]};
    arrayValues: { // can only appear once in filters object
      [key: string]: string[];
    }
  }
}

const example: FiltersStateType = {
  filters: {
    arrayValues: {
      sandwiches: ['ham', 'cheese'],
      milkshakes: ['choc', 'banana'],
    },
    randomString: 'hello',
  },
}

example.filters.arrayValues.sandwiches = example.filters.arrayValues.sandwiches.filter((item: string) => item !== 'ham');
console.log(example.filters.arrayValues.sandwiches);

const stringKey = 'milkshakes'
example.filters.arrayValues[stringKey] = example.filters.arrayValues[stringKey].filter((item: string) => item !== 'choc');
console.log(example.filters.arrayValues[stringKey]);

example.filters.randomString = 'bye';
console.log(example.filters.randomString);


有趣!那么在FiltersStateType中使用"[key: string]: string | {[key: string]: string[]};",联合类型的第二部分是否允许arrayValues对象?为什么不能只是"[key: string]: string"这一行呢? 目前这意味着我可以添加其他具有与arrayValues相同类型的对象...但我不需要。再次感谢! - Hombre Lobo
是的,你说得对,其他属性也可以接受另一种类型!但你不能为一个属性写异常。所以最好的方法是使用@VinaySharma的答案。 - EzioMercer
你的解决方案只是一种消除错误的hack,但它并不能防止其他键具有arrayValues形状。 - Vinay Sharma
@VinaySharma 我知道并且我写过它。 - EzioMercer
@EzioMercer 是的,我注意到了,但如果你的答案实际上并没有"回答"问题,那么最好避免写下来,因为这不会帮助未来的读者,反而会让他们把时间投入到一个不完整的解决方案中 :) - Vinay Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接