索引签名参数类型不能为联合类型。考虑改用映射对象类型

joh*_*thy 18 javascript typescript

我正在尝试使用以下模式:

enum Option {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three'
}

interface OptionRequirement {
  someBool: boolean;
  someString: string;
}

interface OptionRequirements {
  [key: Option]: OptionRequirement;
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎很简单,但是出现以下错误:

索引签名参数类型不能为联合类型。考虑改用映射对象类型。

我究竟做错了什么?

Ame*_*icA 122

就我而言:

\n
export type PossibleKeysType =\n  | \'userAgreement\'\n  | \'privacy\'\n  | \'people\';\n\ninterface ProviderProps {\n  children: React.ReactNode;\n  items: {\n    //   \xe2\x86\x99 this colon was issue\n    [key: PossibleKeysType]: Array<SectionItemsType>;\n  };\n}\n
Run Code Online (Sandbox Code Playgroud)\n

in我通过使用运算符而不是使用来修复它:

\n
~~~\n\ninterface ProviderProps {\n  children: React.ReactNode;\n  items: {\n    //     \xe2\x86\x99 use "in" operator\n    [key in PossibleKeysType]: Array<SectionItemsType>;\n  };\n}\n
Run Code Online (Sandbox Code Playgroud)\n

  • 这对现有答案增加了什么? (4认同)

Nac*_*mos 32

您可以使用TS“ in”运算符,并执行以下操作:

enum Options {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three',
}
interface OptionRequirement {
  someBool: boolean;
  someString: string;
}
interface OptionRequirements {
  [key in Options]: OptionRequirement; // Note that "key in".
}
Run Code Online (Sandbox Code Playgroud)

  • 呃,这不能编译?[TypeScript 游乐场说](https://www.typescriptlang.org/play/index.html#code/KYYOwrgtgBA8gDgFwJYHsQGcoG8BQVYByAolALxQDkawFANHlACoDqMZlCA7inQ4wBIAlIiXIUEACwBOwGvQC+OJCATApAMwCGAY2CxEqEIOABHMEhkRQCbA3QorAIRQoANG C4oAIxevgmkABuOwdgAGUEKWUAc090SJjgxWVVDR09eGQ0YzMLYCsVTFx8AG0Aa2AATyhlfSyMAF1PTMMc80trQKgAem6oAhRVKElNGwAiCurlMYA6HHkgA):“接口中的计算属性名称必须引用其类型为文字类型或一种‘独特的符号’类型。” (16认同)
  • 这实际上对我不起作用:接口中的计算属性名称必须引用其类型为文字类型或“唯一符号”类型的表达式 (8认同)
  • 有人可以解释一下这里发生了什么吗?TS 在将枚举变量设置为键时遇到的根本问题是什么?为什么将“界面”更改为“类型”可以解决问题? (7认同)
  • 将`interface OptionRequirements`更改为`type OptionRequirements`类型 (4认同)
  • 我已编辑此答案以使用映射类型别名而不是接口。原始答案不能在我见过的任何版本的 TypeScript 下编译,当然也不能在当前版本的 TypeScript(截至 2020 年 8 月的 4.0)下编译。@NachoJusticiaRamos,如果您可以证明您的原始版本实际上可以在某些版本的 TypeScript 中的某个地方工作,那么我很乐意恢复编辑,以及对其工作所需的环境的描述。干杯! (3认同)

Kur*_*gor 21

我有一些类似的问题,但我的情况是接口中的另一个字段属性,所以我的解决方案作为一个示例,带有可选字段属性和键枚举:

export enum ACTION_INSTANCE_KEY {
  cat = 'cat',
  dog = 'dog',
  cow = 'cow',
  book = 'book'
}

type ActionInstances = {
  [key in ACTION_INSTANCE_KEY]?: number; // cat id/dog id/cow id/ etc // <== optional
};

export interface EventAnalyticsAction extends ActionInstances { // <== need to be extended
  marker: EVENT_ANALYTIC_ACTION_TYPE; // <== if you wanna add another field to interface
}
Run Code Online (Sandbox Code Playgroud)


uni*_*nal 17

最简单的解决方案是使用 Record

type OptionRequirements = Record<Options, OptionRequirement>
Run Code Online (Sandbox Code Playgroud)

您也可以自己实现为:

type OptionRequirements = {
  [key in Options]: OptionRequirement;
}
Run Code Online (Sandbox Code Playgroud)

此构造仅可用于type,但不可用interface

定义中的问题是接口的键应该是类型Options,其中Options是枚举,而不是字符串,数字或符号。

key in Options“为这是在联合类型选择那些特定的键”的意思。

type别名比别名更灵活和强大interface

如果你的类型并不需要在课堂上使用,选择typeinterface

  • 另外,我想知道是否有一种方法可以强制将密钥放在枚举中,但不会让打字稿抱怨您必须详尽地使用该枚举的所有成员... (6认同)

Ste*_*fan 15

使用映射对象类型而不是使用接口

enum Option {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three'
}

type OptionKeys = keyof typeof Option;

interface OptionRequirement {
  someBool: boolean;
  someString: string;
}

type OptionRequirements = {                 // note type, not interface
  [key in OptionKeys]: OptionRequirement;   // key in
}
Run Code Online (Sandbox Code Playgroud)


小智 11

在我的情况下,我需要属性是可选的,所以我创建了这个通用类型。

type PartialRecord<K extends string | number | symbol, T> = { [P in K]?: T; };
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

type MyTypes = 'TYPE_A' | 'TYPE_B' | 'TYPE_C';

interface IContent {
    name: string;
    age: number;
}

interface IExample {
    type: string;
    partials: PartialRecord<MyTypes, IContent>;
}
Run Code Online (Sandbox Code Playgroud)

例子

const example : IExample = {
    type: 'some-type',
    partials: {
        TYPE_A : {
            name: 'name',
            age: 30
        },
        TYPE_C : {
            name: 'another name',
            age: 50
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Joe*_*Joe 9

已编辑

TL;DR:使用Record<type1,type2>或映射对象,例如:

type YourMapper = {
    [key in YourEnum]: SomeType
}
Run Code Online (Sandbox Code Playgroud)

我遇到了类似的问题,问题是键允许的类型是字符串、数字、符号或模板文字类型。

因此,正如 Typescript 所建议的,我们可以使用映射的对象类型:

type Mapper = {
    [key: string]: string;
}
Run Code Online (Sandbox Code Playgroud)

请注意,在映射对象中,我们只允许使用字符串、数字或符号作为键,因此如果我们想使用特定字符串(即 emum 或 union 类型),我们应该in在索引签名中使用关键字。这用于引用枚举或联合中的特定属性。

type EnumMapper = {
  [key in SomeEnum]: AnotherType;
};
Run Code Online (Sandbox Code Playgroud)

在现实生活中的例子中,假设我们想要得到这个结果,一个它的键和值都是指定类型的对象:

  const notificationMapper: TNotificationMapper = {
    pending: {
      status: EStatuses.PENDING,
      title: `${ENotificationTitels.SENDING}...`,
      message: 'loading message...',
    },
    success: {
      status: EStatuses.SUCCESS,
      title: ENotificationTitels.SUCCESS,
      message: 'success message...',
    },
    error: {
      status: EStatuses.ERROR,
      title: ENotificationTitels.ERROR,
      message: 'error message...'
    },
  };
Run Code Online (Sandbox Code Playgroud)

为了使用 Typescript 实现此目的,我们应该创建不同的类型,然后在 Record<> 中或使用映射的对象类型来实现它们:

export enum EStatuses {
  PENDING = 'pending',
  SUCCESS = 'success',
  ERROR = 'error',
}

interface INotificationStatus {
  status: string;
  title: string;
  message: string;
}

//option one, Record:
type TNotificationMapper = Record<EStatuses, INotificationStatus>

//option two, mapped object:
type TNotificationMapper = {
  [key in EStatuses]:INotificationStatus;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我使用枚举,但这种方法适用于枚举和联合类型。

*注意-使用括号而不是方括号的类似语法(即 this(...)而不是 this [...],可能不会显示任何错误,但它表示完全不同的东西,一个函数接口,所以这样:

interface Foo {
(arg:string):string;
}
Run Code Online (Sandbox Code Playgroud)

实际上是描述一个函数签名,例如:

const foo = (arg:string) => string;
Run Code Online (Sandbox Code Playgroud)