在同一接口中基于另一个属性定义属性的类型

时间:2019-06-30 17:53:36

标签: typescript generics interface

我有一个非常简单的界面

type MessageType = 'name' | 'age';
type MessageData = string | number 

interface Message {
    type: MessageType;
    data: MessageData;
}

我现在想要实现的是,当创建一个实现类型为Message的{​​{1}}的对象时,要让TypeScript知道name的类型必须是{ {1}},如果不是这种情况,则将其掉毛。

data

我通过这种实现方式实现了这一点

string

但是,我不想提供const message: Message = { type: 'name', data: 'Michael' } ,而是让type MessageType = 'name' | 'age'; type MessageData<T extends MessageType> = T extends 'name' ? string : number; interface Message<T extends MessageType> { type: T; data: MessageData<T>; } const message: Message<'name'> = { type: 'name', data: 'Michael' } 使用我为MessageType传递的值。

您可以尝试这种情况here

灵魂

借助jcalz找到了高度可扩展的解决方案:

Message

1 个答案:

答案 0 :(得分:2)

我将使用类型别名代替Message的接口,因为您自然是在谈论discriminated union,并且接口不能表示并集。我会这样定义:

type Message = {
  [K in MessageType]: { type: K; data: MessageData<K> }
}[MessageType];

第一部分是mapped type,它产生像{name: {type: "name", data: string}, age: {type: "age", data: number}这样的对象,最后一部分是lookup type,它得到该类型的值的并集...即{ {1}}。然后,这应该可以按您期望的那样工作:

{type: "name", data: string} | {type: "age", data: number}

Link to code

请注意,您的const desiredMessage: Message = { type: "name", data: "Michael" }; const alsoDesiredMessage: Message = { type: "age", data: 14 }; const badMessage: Message = { type: "name", data: 14 }; // error! number is not string MessageType类型在某种程度上间接地表示了您的约束...如果MessageData开始增长,MessageData的条件类型可能无法很好地扩展。相反,我建议使用这样的帮助程序界面:

MessageType

然后,您的interface MessageDataMap { name: string; age: number; } MessageType可以表示为MessageData /查找类型

keyof

其行为与以前完全相同,但是您可以相对轻松地向type MessageType = keyof MessageDataMap type MessageData<T extends MessageType> = MessageDataMap[T] 添加属性。

Link to code

好的,希望能有所帮助;祝你好运!

相关问题