我有一个非常简单的界面
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
答案 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}
请注意,您的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]
添加属性。
好的,希望能有所帮助;祝你好运!