【问题标题】:Typescript recursive object type打字稿递归对象类型
【发布时间】:2018-03-22 22:16:29
【问题描述】:

所以我正在尝试定义一个可以具有相同类型的嵌套属性的类型/接口。我的代码:

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

interface TMessages {
  messages: TMessagesFormat;
}

interface TGroupMessages {
  messages?: TMessagesFormat;
  controls: { [key: string]: TMessages | TGroupMessages }
}

let groupMessages: TGroupMessages = {
  controls: {
    username: { messages: {required: 'Username required'} }
  }
}

let messages: TGroupMessages = {
  controls: {
    username: { messages: { required: 'Username required' } },
    passwordGroup: {
      messages: { nomatch: 'Passwords doesn\'t match' },
      controls: {
        password: { messages: { required: 'Password required' } }
      }
    }
  }
};

类型检查适用于用户名和密码组,但例如密码组中的控件可以是任何东西,并且 TS 编译器不会抱怨。事实上,如果我将 controls: 'whatever' 属性(字符串不应该是有效类型)放在 username 对象字面量中,代码仍然可以编译而没有任何警告或错误。这可能吗?如何?谢谢!

【问题讨论】:

  • 猜猜有一个隐式类型断言。让 x :TMessages = { 消息:{必需:''},控件:1 }; // 抛出错误 let y = { messages: {required: ''}, controls: 1 } as TMessages; // 效果很好

标签: typescript


【解决方案1】:

看起来这是a bug in TypeScript,其中对对象字面量的过多属性检查似乎不会像您期望的那样在联合中发生。最短复制:

interface A {
    a: string;
}
interface B {
    b: number;
}
const a: A = {a: 'dog', b: 'cat'}; // error, b is unknown property
const ab: A | B = {a: 'dog', b: 'cat'}; // no error!

您会认为ab 会出现'cat' is not a number 之类的错误,但这并不是因为上述问题。如果该问题得到解决,您的问题应该会消失。


但我们不必等待。事实上,对对象字面量的过多属性检查并不是非常 保护性的。如果您将未知属性添加到 "fresh" 对象文字,它只会警告您。在 TypeScript 中,向对象添加额外属性并没有什么问题。如果一个对象是一个有效的A,那么即使你向它添加了额外的属性,同一个对象仍然是一个有效的A。因此,如果您采取措施避免过多的属性检查(例如,为其分配“非新鲜”文字),则可以添加任何您想要的额外属性。

const dogCat = {a: 'dog', b: 'cat'};
const a: A  = dogCat; // no error

如果你真的想禁止额外的属性怎么办?好吧,没有general way 可以为所有属性执行此操作。但是,如果您想禁止 特定 额外属性并知道它们的键名,有一种方法可以做到:

interface A {
    a: string;
    b?: never; // cannot have a defined b
}
interface B {
    b: number;
}
const dogCat = {a: 'dog', b: 'cat'};
const a: A  = dogCat; // error, string is not undefined
const ab: A | B = {a: 'dog', b: 'cat'}; // error, string is not number

现在您会收到非新鲜的aab 的错误。所以,回到你的情况......


如果我们可以假设TMessages 不能拥有controls 属性:

interface TMessages {
  messages: TMessagesFormat;
  controls?: never; // no defined controls
}

interface TGroupMessages {
  messages?: TMessagesFormat;
  controls: { [key: string]: TMessages | TGroupMessages }
}

那么,如果TMessages | TGroupMessages 具有定义的controls 属性,则它必须是TGroupMessages 中指定的类型:

let tm: TMessages | TGroupMessages = {messages: {foo: 'hey'}, controls: 3}
// error, 'number' not assignable to type '{ [k: string]: TMessages | TGroupMessages }'

这应该对你有用。希望有帮助;祝你好运!


TL;DR

等待Microsoft/TypeScript#22129 被寻址,或将您的TMessages 接口更改为以下内容:

interface TMessages {
  messages: TMessagesFormat;
  controls?: never; // no defined controls
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2021-06-30
    • 2018-09-15
    • 2020-06-10
    • 1970-01-01
    相关资源
    最近更新 更多