使用相同的键键入不同类型的值的对象

问题描述 投票:0回答:1

我有一组存储源数据的 JSON 文件。一种语言有一个文件,所有文件都具有相同的键但具有不同类型的值。是这样的:

// source-en.json
{
  "action": ["change", "check"],
  "device": ["coil", "relay"],
}
// source-pl.json
{
  "action": ["wymienić", "sprawdzić"],
  "device": [
    {
      "gender": "f",
      "singular": "cewkę",
      "plural": "cewki"
    },
    {
      "gender": "m",
      "singular": "przekaźnik",
      "plural": "przekaźniki"
    }
  ]
}

所以,我可以用接口来描述它们:

interface SourceEn {
  action: string[],
  device: string[],
}

interface SourcePl {
  action: string[],
  device: NounForms[],
}

interface NounForms {
  gender: 'm' | 'f' | 'n' | 'pl';
  singular: string;
  plural: string;
}

现在,我有了一组使用数据的类:

EnConsumer
PlConsumer
...继承自
BaseConsumer
BaseConsumer
知道如何获取源文件并将 JSON 转换为实际数据,而具体类知道如何处理特定于语言的差异。所以,
BaseConsumer
看起来像这样:

type Source = SourceEn | SourcePl;

class BaseConsumer<T extends Source> {
  public readonly action: Collection<ArrayItem<T['action']>>;
  public readonly device: Collection<ArrayItem<T['device']>>;

  public constructor(language: Language) {
    const data = this.getData(language);
    this.action = new Collection(data.action);
    this.device = new Collection(data.device);
  }
  
  protected getData(language: Language): T {...}
}

class Collection<T> {
  public constructor(private store: T[]) {}
}

type ArrayItem<A extends unknown[]> = A extends (infer E)[] ? E : never;

enum Language {
  EN = 'en',
  PL = 'pl',
}

但这不起作用。具体来说,

this.device = new Collection(data.device);
行给出了两个错误:

  1. data.device

string[] | NounForms[]
类型的参数不可分配给
string[]
类型的参数
类型
NounForms[]
不可分配给类型
string[]

类型
NounForms
不可分配给类型
string

  1. this.device

类型

Collection<string>
不可分配给类型
Collection<Source['device']>

类型
string
不可分配给类型
Source['device']

问题源于以下事实:

T extends Source
代表
SourceEn
SourcePl
的并集。但对于
class ConsumerPl extends BaseConsumer<SourcePl>
ConsumerPl
类应该知道哪些接口实际适用,以及哪些类型应适用于相应的字段。

我不知道如何用 TypeScript 表达它。我不想太明确,因为今天它支持英语和波兰语,但明天也可能支持克林贡语和昆雅语。因此,冗长的条件类型不是有效的解决方案。那么我怎样才能使

BaseConsumer
正确通用呢?

typescript
1个回答
0
投票

这可行,但是如果没有看到您的

getData
方法,很难准确说出您在寻找什么。

interface SourceEn {
    action: string[],
    device: string[],
}

interface SourcePl {
    action: string[],
    device: NounForms[],
}

interface NounForms {
    gender: 'm' | 'f' | 'n' | 'pl';
    singular: string;
    plural: string;
}

type Source = SourceEn | SourcePl;

class Collection<T> {
  public constructor(private store: T) {} // Add the array back in here if that's your intention, but then use the commented out return value below.
}

type ArrayItem<A extends unknown[]> = A extends (infer E)[] ? E : never;

enum Language {
  EN = 'en',
  PL = 'pl',
}

class BaseConsumer<T extends Source> {

    public readonly action: Collection<T['action']>;
    public readonly device: Collection<T['device']>;

    public constructor(language: Language) {
        const data: T = this.getData(language)
        // this.action = new Collection<typeof data.action>([data.action]); Either this one if you want an array of arrays.
        this.action = new Collection<typeof data.action>(data.action); // Or this one, but then I removed the array from your Collection class.
        this.device = new Collection<typeof data.device>(data.device);
    }

    protected getData(language: Language): T { return null! as T }
}
© www.soinside.com 2019 - 2024. All rights reserved.