[英]Define type of a property based on another property in the same interface
我有一个非常简单的界面
type MessageType = 'name' | 'age';
type MessageData = string | number
interface Message {
type: MessageType;
data: MessageData;
}
我现在想要实现的是,当创建一个对象来实现类型为name
Message
时,要让TypeScript知道,该data
必须为string
类型,如果不是,则将其lint。
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
,而是让Message
使用我传递给type
的值。
您可以在这里尝试这种情况
SOULTION
借助jcalz找到了高度可扩展的解决方案:
interface MessageDataMap {
name: string;
age: number;
gender: 'male' | 'female';
// ....
}
type MessageType = keyof MessageDataMap;
type Message = {
[K in MessageType]: {
type: K;
data: MessageDataMap[K];
};
}[MessageType];
我会使用Type别名而不是Message
的接口,因为您自然在谈论一个有区别的联合 ,而接口不能代表联合。 我会这样定义:
type Message = {
[K in MessageType]: { type: K; data: MessageData<K> }
}[MessageType];
第一部分是映射类型 ,它产生一个对象,例如{name: {type: "name", data: string}, age: {type: "age", data: number}
,最后一部分是一个查找类型 ,获取该类型的值的并集...即{type: "name", data: string} | {type: "age", data: number}
{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
类型在某种程度上间接表示您的约束...如果MessageType
开始增长, MessageData
的条件类型可能无法很好地扩展。 相反,我建议使用这样的帮助程序界面:
interface MessageDataMap {
name: string;
age: number;
}
然后,你的MessageType
和MessageData
可以表示为keyof
/查找类型
type MessageType = keyof MessageDataMap
type MessageData<T extends MessageType> = MessageDataMap[T]
这与以前完全一样,但是您可以相对轻松地向MessageDataMap
添加属性。
好的,希望能有所帮助; 祝好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.