How can I define an interface for a object/array containing key/value pairs as well as nested objects?

半腔热情 提交于 2021-01-29 22:23:32

问题


I'm typing up a localization-library, and I'm aiming to have it strongly typed (as it will be re-used in multiple angular-applications) and - at the same time - backwards compatible so that we won't have to re-write all existing localization-files.

However, the structure of said localization-files is causing me a bit of a headache. Per example:

{
  'any-random-key': 'This is a string',
  'another-random-key': {
    'random-key': 'This is a string'
  },
  'yet-another-random-key': {
    'random-key-2': 'This is a string',
    'random-key-3': {
      'random-key-4': 'This is a string',
      'random-key-5': {
        'random-key-6': 'This is a string'
      }
    }
  },
  'and-yet-another-random-key': {
    'random-key-6': {
      'random-key-7': {
        'random-key-8': 'This is a string'
      },
      'random-key-9': 'This is a string'
    }
  }
}

Now - I suppose I could say that the service accepts translations: any or translations: object - but that's a little too random (no pun intended) for my liking.

So I tried using two different interfaces:

export interface ITranslation {
  [s: string]: string;
}

export interface ITranslations {
  [s: string]: ITranslation;
}

However that fails on any-random-key saying: Type 'string' is not assignable to type 'ITranslation'

So I tweak my ITranslations-interface so that it becomes

export interface ITranslations {
  [s: string]: ITranslation | string;
}

Which fixes the above error, but introduces a new one on 'and-yet-another-random-key' saying Property ''and-yet-another-random-key'' is incompatible with index signature.

At this point I am a little stumped. Is what I am trying to achieve (strong typing of the legacy structure) simply not plausible?


回答1:


For any arbitrary level of nesting (in other words, your data object can be as many levels deep as you want), you can simply self-reference the interface as such:

/** @interface */
export interface ITranslations {
  [s: string]: ITranslations | string;
}

See the above example on TypeScript playground.


If you want to only allow 3-level deep nesting, then the interface will have to be verbose: TypeScript does not allow you to define how "deep" (i.e. the degree of nesting):

/** @interface */
export interface ITranslations<T = string> {
  [s: string]: T | string;
}

/** @type */
export type ITranslationsMax3Levels = ITranslations<ITranslations<ITranslations<ITranslations>>>;

const data: ITranslationsMax3Levels = { ... }

See the above example on TypeScript playground.




回答2:


Seems the solution was easier than I dared hope for:

export interface ITranslations {
  [s: string]: ITranslations | ITranslation | string;
}


来源:https://stackoverflow.com/questions/57267444/how-can-i-define-an-interface-for-a-object-array-containing-key-value-pairs-as-w

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!