可能是一个奇怪的问题,但我很好奇是否可以创建一个需要一个属性或另一个属性的接口。
所以,例如......
interface Message {
text: string;
attachment: Attachment;
timestamp?: number;
// ...etc
}
interface Attachment {...}
在上述情况下,我想确保text
或attachment
存在。
希望这是有道理的。
提前致谢!
编辑:这就是我现在正在做的事情。认为它有点冗长(键入botkit for slack)。
interface Message {
type?: string;
channel?: string;
user?: string;
text?: string;
attachments?: Slack.Attachment[];
ts?: string;
team?: string;
event?: string;
match?: [string, {index: number}, {input: string}];
}
interface AttachmentMessageNoContext extends Message {
channel: string;
attachments: Slack.Attachment[];
}
interface TextMessageNoContext extends Message {
channel: string;
text: string;
}
答案 0 :(得分:43)
如果您确实是在“一个或另一个属性”之后,那么可以在扩展类型中使用never
:
interface MessageBasics {
timestamp?: number;
/* more general properties here */
}
interface MessageWithText extends MessageBasics {
text: string;
attachment?: never;
}
interface MessageWithAttachment extends MessageBasics {
text?: never;
attachment: string;
}
type Message = MessageWithText | MessageWithAttachment;
// ? OK
let foo: Message = {attachment: 'a'}
// ? OK
let bar: Message = {text: 'b'}
// ❌ ERROR: Type '{ attachment: string; text: string; }' is not assignable to type 'Message'.
let baz: Message = {attachment: 'a', text: 'b'}
答案 1 :(得分:39)
您可以使用联合类型执行此操作:
import sample
如果你想同时允许文字和附件,你可以写
interface MessageBasics {
timestamp?: number;
/* more general properties here */
}
interface MessageWithText extends MessageBasics {
text: string;
}
interface MessageWithAttachment extends MessageBasics {
attachment: Attachment;
}
type Message = MessageWithText | MessageWithAttachment;
答案 2 :(得分:5)
感谢@ ryan-cavanaugh让我朝着正确的方向前进。
我有类似的情况,但随后是数组类型。在语法上有点挣扎,所以我把它放在这里供以后参考:
interface BaseRule {
optionalProp?: number
}
interface RuleA extends BaseRule {
requiredPropA: string
}
interface RuleB extends BaseRule {
requiredPropB: string
}
type SpecialRules = Array<RuleA | RuleB>
// or
type SpecialRules = (RuleA | RuleB)[]
// or (in the strict linted project I'm in):
type SpecialRule = RuleA | RuleB
type SpecialRules = SpecialRule[]
更新
请注意,稍后,您可能会在代码中使用声明的变量时收到警告。然后,您可以使用(variable as type)
语法。
例如:
const myRules: SpecialRules = [
{
optionalProp: 123,
requiredPropA: 'This object is of type RuleA'
},
{
requiredPropB: 'This object is of type RuleB'
}
]
myRules.map((rule) => {
if ((rule as RuleA).requiredPropA) {
// do stuff
} else {
// do other stuff
}
})
答案 3 :(得分:4)
您可以更深入地使用 @robstarbuck 解决方案创建以下类型:
type Only<T, U> = {
[P in keyof T]: T[P];
} & {
[P in keyof U]?: never;
};
type Either<T, U> = Only<T, U> | Only<U, T>;
然后 Message
类型看起来像这样
interface MessageBasics {
timestamp?: number;
/* more general properties here */
}
interface MessageWithText extends MessageBasics {
text: string;
}
interface MessageWithAttachment extends MessageBasics {
attachment: string;
}
type Message = Either<MessageWithText, MessageWithAttachment>;
使用此解决方案,您可以轻松地在 MessageWithText
或 MessageWithAttachment
类型中添加更多字段,而无需将其排除在其他类型中。
答案 4 :(得分:2)
好吧,所以经过反复试验和谷歌搜索后,我发现答案没有达到我的用例预期的效果。因此,如果有人遇到同样的问题,我想我应该分享一下我如何使其工作。我的界面是这样的:
export interface MainProps {
prop1?: string;
prop2?: string;
prop3: string;
}
我要寻找的是一个类型定义,它将说我们既不能定义prop1也不能定义prop2。我们可以定义prop1,但不能定义prop2。最后定义了prop2,但没有prop1。这是我发现的解决方案。
interface MainBase {
prop3: string;
}
interface MainWithProp1 {
prop1: string;
}
interface MainWithProp2 {
prop2: string;
}
export type MainProps = MainBase | (MainBase & MainWithProp1) | (MainBase & MainWithProp2);
这很完美,除了一个警告是,当我尝试在另一个文件中引用prop1或prop2时,我一直获取的属性不存在TS错误。这是我如何解决这个问题的方法:
import {MainProps} from 'location/MainProps';
const namedFunction = (props: MainProps) => {
if('prop1' in props){
doSomethingWith(props.prop1);
} else if ('prop2' in props){
doSomethingWith(props.prop2);
} else {
// neither prop1 nor prop2 are defined
}
}
只是以为我会分享这一点,因为如果我碰到一点点怪异,那么其他人也可能也是。
答案 5 :(得分:1)
您可以为所需的条件创建一些接口,并以如下所示的类型将其加入:
interface SolidPart {
name: string;
surname: string;
action: 'add' | 'edit' | 'delete';
id?: number;
}
interface WithId {
action: 'edit' | 'delete';
id: number;
}
interface WithoutId {
action: 'add';
id?: number;
}
export type Entity = SolidPart & (WithId | WithoutId);
const item: Entity = { // valid
name: 'John',
surname: 'Doe',
action: 'add'
}
const item: Entity = { // not valid, id required for action === 'edit'
name: 'John',
surname: 'Doe',
action: 'edit'
}
答案 6 :(得分:1)
在寻找针对我的案例的答案(propA,propB或都不选择)时,我偶然发现了这个线程。瑞安·藤原(Ryan Fujiwara)的回答几乎做到了,但是我已经失去了一些支票。
我的解决方案:
interface ComponentBase {
baseProp: string;
}
interface ComponentWithPropA extends Base {
propA: string;
propB?: never;
}
interface ComponentWithPropB extends Base {
propB: string;
propA?: never;
}
interface ComponentWithoutProps extends Base {
propA?: never;
propB?: never;
}
type ComponentProps = ComponentWithPropA | ComponentWithPropB | ComponentWithoutProps;
此解决方案将所有检查保持应有的状态。也许有人会觉得这很有用:)
答案 7 :(得分:0)
有一些不错的Typescript选项,您可以使用https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk
您的问题是:建立一个接口,其中存在“文本”或附件。您可以执行以下操作:
interface AllMessageProperties {
text: string,
attachement: string,
}
type Message = Omit<AllMessageProperties, 'text'> | Omit<AllMessageProperties, 'attachement'>;
const messageWithText : Message = {
text: 'some text'
}
const messageWithAttachement : Message = {
attachement: 'path-to/attachment'
}
const messageWithTextAndAttachement : Message = {
text: 'some text',
attachement: 'path-to/attachment'
}
// results in Typescript error
const messageWithOutTextOrAttachement : Message = {
}