用于检测重复对象(例如多语言字段)中缺失属性的类型定义规则

A type-definition rule to detect missing properties in repeating objects (e.g. multi language fields)

假设我有这样一个类型定义:

export type TextMessagesType = {
  [language: string]: {
    [placeholder: string]: string;
  };
};

适合这种情况:

export const TextMessages: TextMessagesType = {
  en: {
    noText: 'No Texts available!',
  },
};

现在,如果我想添加新语言和新属性,例如:

export const TextMessages: TextMessagesType = {
  en: {
    noText: 'No Texts available!',
    welcome: 'You are welcome'
  },
  de: {
    noText: 'Keine weiteren Texte vorhanden!',
    // welcome missing
  },
};

我想确保 de-对象与 en-对象具有完全相同的属性。由于类型定义,IDE 应该识别缺失的属性(例如,welcome)。

我可以借助打字稿类型定义规则来做到这一点吗?如果是,如何?

编辑:对不起,我想,缺少一个重要信息:

识别机制应该依赖于每个对象中的现有属性。假设 de 对象有一个 属性 xy 而它在 en 对象中丢失,反之亦然。如果一个语言对象获得了一个新的 属性,它应该在所有其他语言对象中被标记为缺失 属性。

我们可以使用联合类型的字符串来实现这一点,在键的类型上使用 in 关键字:

type RequiredLanguageFields = 'welcome'
  | 'noText';

type TextMessagesType = {
  [language: string]: {
    [placeholder in RequiredLanguageFields]: string;
  };
};

const TextMessages: TextMessagesType = {
  en: {
    noText: 'No Texts available!',
    welcome: 'You are welcome'
  },
  de: {   // type error on this line
    noText: 'Keine weiteren Texte vorhanden!',
    // welcome missing
  },
};

Property 'welcome' is missing in type '{ noText: string; }' but required in type '{ welcome: string; noText: string; }'.(2741)

需要做一些额外的工作,因为您需要先定义所需的字段,然后再将它们添加到对象中。或者你可以有一些主翻译对象并使用它的键来定义其他所需的键:

const enStrings = {
  noText: 'No Texts available!',
  welcome: 'You are welcome',
};

type TextMessagesType = {
  [language: string]: {
    [placeholder in keyof typeof enStrings]: string;
  };
};

根据对您问题的修改,我将尝试描述为什么我认为无法按照您希望的方式创建类型。

现在我们说所有值都必须是同一类型的对象 - 它们必须都具有相同的属性,并且所有这些属性都必须是字符串。但那是什么类型?我们可能会定义一些采用通用接口的接口:

interface ITextMessagesType<T> {
  [language: string]: {
    [placeholder in keyof T]: string;
  };
};

const TextMessages: ITextMessagesType = {    // error here as we have no passed in the type for the generic `T`
  en: {
    noText: 'No Texts available!',
    welcome: 'You are welcome'
  },
  de: {   // type error on this line
    noText: 'Keine weiteren Texte vorhanden!',
    // welcome missing
  },
};

我们仍然需要定义泛型是什么;我们回到您在上面给出的原始示例中遇到的问题 - 您需要在定义对象之前定义键。

在函数中更容易一些,因为我们可以从传入的对象中推断出类型。但是接下来我们进入下一个问题;哪个对象被视为所需类型?以下面的代码为例:

const test = <T>(x: { [key: string]: { [key in keyof T]: string } }) => true;

const x = test({
  en: {
    noText: 'No Texts available!',
    welcome: 'You are welcome',     // now we get a type error here
  },
  de: {
    noText: 'Keine weiteren Texte vorhanden!',
    // welcome missing
  },
})

我们得到的错误是:

Type '{ noText: string; welcome: string; }' is not assignable to type '{ noText: string; }'. Object literal may only specify known properties, and 'welcome' does not exist in type '{ noText: string; }'.(2322)

此处 Typescript 已确定 de 处的值是 'master' 类型 - 因此在我们尝试在 welcome 上定义键时出现错误=17=].

因此我不相信你能得到你想要的东西 - 希望有人会进来证明我错了。