typescript 索引签名参数类型不能是联合类型,请考虑改用Map的对象类型

zzoitvuj  于 2022-12-14  发布在  TypeScript
关注(0)|答案(8)|浏览(216)

我尝试使用以下模式:

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

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

interface OptionRequirements {
  [key: Option]: OptionRequirement;
}

这对我来说似乎很简单,但是我得到了以下错误:
索引签名参数类型不能是联合类型.请考虑改用Map得对象类型.
我做错了什么?

xj3cbfub

xj3cbfub1#

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

enum Options {
  ONE = 'one',
  TWO = 'two',
  THREE = 'three',
}
interface OptionRequirement {
  someBool: boolean;
  someString: string;
}
type OptionRequirements = {
  [key in Options]: OptionRequirement; // Note that "key in".
}
oipij1gg

oipij1gg2#

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

type OptionRequirements = Record<Options, OptionRequirement>

您也可以自己将其实现为:

type OptionRequirements = {
  [key in Options]: OptionRequirement;
}

此构造仅适用于type,而不适用于interface
定义中的问题是接口的键应该是Options类型,其中Options是枚举,而不是字符串、数字或符号。
key in Options表示“对于联合类型Options中的那些特定键”。
type别名比interface更加灵活和强大。
如果您的型别不需要在类别中使用,请选择type而非interface

up9lanfz

up9lanfz3#

在我的例子中:

export type PossibleKeysType =
  | 'userAgreement'
  | 'privacy'
  | 'people';

interface ProviderProps {
  children: React.ReactNode;
  items: {
    //   ↙ this colon was issue
    [key: PossibleKeysType]: Array<SectionItemsType>;
  };
}

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

~~~

interface ProviderProps {
  children: React.ReactNode;
  items: {
    //     ↙ use "in" operator
    [key in PossibleKeysType]: Array<SectionItemsType>;
  };
}
lmyy7pcs

lmyy7pcs4#

我遇到过一些类似问题,但我的情况是接口中的另一个字段属性,因此我的解决方案是使用optional字段属性和键枚举的示例:

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
}
brjng4g3

brjng4g35#

请使用Map的对象类型,而不要使用接口

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
}
eoxn13cs

eoxn13cs6#

在我的例子中,我需要属性是可选的,所以我创建了这个泛型类型。

type PartialRecord<K extends string | number | symbol, T> = { [P in K]?: T; };

然后按如下方式使用它:

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

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

interface IExample {
    type: string;
    partials: PartialRecord<MyTypes, IContent>;
}

范例

const example : IExample = {
    type: 'some-type',
    partials: {
        TYPE_A : {
            name: 'name',
            age: 30
        },
        TYPE_C : {
            name: 'another name',
            age: 50
        }
    }
}
vsikbqxv

vsikbqxv7#

我也遇到过类似的问题。我试图在创建角形验证器时只使用特定的键。

export enum FormErrorEnum {
  unknown = 'unknown',
  customError = 'customError',
}

export type FormError = keyof typeof FormErrorEnum;

用法:

static customFunction(param: number, param2: string): ValidatorFn {
  return (control: AbstractControl): { [key: FormErrorEnum]?: any } => {
    return { customError: {param, param2} };
  };
}

这将允许使用1 - X个密钥。

vlju58qv

vlju58qv8#

已编辑

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

type YourMapper = {
    [key in YourEnum]: SomeType
}

我也遇到过类似的问题,问题是允许的键类型是字符串、数字、符号或模板文字类型。
因此,正如Typescript所建议的,我们可以使用Map的对象类型:

type Mapper = {
    [key: string]: string;
}

注意在一个Map对象中我们只允许使用字符串、数字或符号作为键,所以如果我们想使用一个特定的字符串(即枚举或联合类型),我们应该在索引签名中使用in关键字。这是用来引用枚举或联合中的特定属性。

type EnumMapper = {
  [key in SomeEnum]: AnotherType;
};

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

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...'
    },
  };

为了使用Typescript实现这一点,我们应该创建不同的类型,然后在Record〈〉中实现它们,或者使用Map的对象类型:

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;
}

这里我使用了枚举,但是这种方法对枚举和联合类型都有效。

  • 注意-使用括号而不是方括号的类似语法(即,使用此(...)而不是此[...])可能不会显示任何错误,但它表示一个完全不同的东西,一个函数接口,因此:
interface Foo {
(arg:string):string;
}

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

const foo = (arg:string) => string;

相关问题