在 TypeScript 允许您读取name
或color
关闭联合类型之前,它需要一些证据证明您正在使用正确类型的 Props(SamplePropsOne
或SamplePropsTwo
)。有一些标准方法可以提供它。
一种是通过引入属性来区分联合的分支,从而使联合成为标记联合。这种类型检查得很好:
interface SamplePropsOne {
type: 'one';
name: string;
}
interface SamplePropsTwo {
type: 'two';
color: string;
}
type Props = SamplePropsOne | SamplePropsTwo;
const SampleComponent: React.FC<Props> = props => (
props.type === 'one' ? (
<h1>{props.name}</h1>
) : (
<h1>{props.color}</h1>
)
);
如果你把案例倒过来(就像我在写这篇文章时所做的那样!)然后 TypeScript 会抱怨。
如果属性的存在足以区分类型,那么您可以使用in
运算符:
interface SamplePropsOne {
name: string;
}
interface SamplePropsTwo {
color: string;
}
type Props = SamplePropsOne | SamplePropsTwo;
const SampleComponent: React.FC<Props> = props => (
'color' in props ? (
<h1>{props.color}</h1>
) : (
<h1>{props.name}</h1>
)
);
如果确定您拥有哪种类型的对象需要更复杂的逻辑,您可以编写一个用户定义的类型保护。关键部分是返回类型中的“is”:
function isSampleOne(props: Props): props is SamplePropsOne {
return 'name' in props;
}
const SampleComponent: React.FC<Props> = props => (
isSampleOne(props) ? (
<h1>{props.name}</h1>
) : (
<h1>{props.color}</h1>
)
);
还值得注意的是,由于结构类型的工作方式,props
您的示例中没有理由不能同时具有name
and color
:
const el = <SampleComponent name="roses" color="red" />; // ok
如果不允许这样做很重要,您将需要使用一些更高级的类型:
interface SamplePropsOne {
name: string;
color?: never;
}
interface SamplePropsTwo {
color: string;
name?: never;
}
type Props = SamplePropsOne | SamplePropsTwo;
ts-essentials 库有一个XOR
泛型,可以用来帮助构建这样的独占联合。