我有一组存储源数据的 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);
行给出了两个错误:
data.device
:类型的参数不可分配给string[] | NounForms[]
类型的参数string[]
类型不可分配给类型NounForms[]
string[]
类型不可分配给类型NounForms
string
this.device
:类型
不可分配给类型Collection<string>
Collection<Source['device']>
类型不可分配给类型string
Source['device']
问题源于以下事实:
T extends Source
代表SourceEn
和SourcePl
的并集。但对于 class ConsumerPl extends BaseConsumer<SourcePl>
,ConsumerPl
类应该知道哪些接口实际适用,以及哪些类型应适用于相应的字段。
我不知道如何用 TypeScript 表达它。我不想太明确,因为今天它支持英语和波兰语,但明天也可能支持克林贡语和昆雅语。因此,冗长的条件类型不是有效的解决方案。那么我怎样才能使
BaseConsumer
正确通用呢?
这可行,但是如果没有看到您的
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 }
}