这可能是一个奇怪的问题,但我很好奇是否有可能创建一个需要其中一个属性的接口。
所以,例如……
interface Message {
text: string;
attachment: Attachment;
timestamp?: number;
// ...etc
}
interface Attachment {...}
在上面的情况下,我想确保文本或附件都存在。
我现在就是这么做的。我觉得它有点啰嗦(为slack输入botkit)。
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;
}
您可以为所需的条件创建一些接口,并将它们以如下类型连接起来:
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'
}
谢谢@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[]
更新:
请注意,稍后在代码中使用声明的变量时,可能仍然会收到警告。然后可以使用(变量作为类型)语法。
例子:
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
}
})
下面是一个非常简单的实用程序类型,我使用它来创建一个新类型,允许多个接口/类型中的一个被称为InterfacesUnion:
export type InterfacesUnion<Interfaces> = BuildUniqueInterfaces<UnionToIntersection<Interfaces>, Interfaces>;
type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
export type BuildUniqueInterfaces<CompleteInterface, Interfaces> = Interfaces extends object
? AssignNever<CompleteInterface, Interfaces>
: never;
type AssignNever<T, K> = K & {[B in Exclude<keyof T, keyof K>]?: never};
它可以这样使用:
type NewType = InterfacesUnion<AttachmentMessageNoContext | TextMessageNoContext>
它通过接受接口/类型的联合来运行,构建一个包含它们所有属性的单一接口,并返回相同的接口/类型的联合,其中每个接口都包含其他接口拥有而它们没有的附加属性,这些属性被设置为可选的never ([propertyName]?:永远不要)。
有例子/解释的游乐场
好吧,经过一段时间的尝试和错误,我发现答案并没有像我的用例预期的那样工作。所以为了防止其他人有同样的问题,我想我应该分享我是如何让它工作的。我的界面是这样的:
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
}
}
我只是想分享一下,因为如果我遇到了这种奇怪的事情,那么其他人可能也会遇到。