reactjs - Typescript - 如何组合 Union 和 Intersection 类型
问题描述
我有以下组件:
export enum Tags {
button = 'button',
a = 'a',
input = 'input',
}
type ButtonProps = {
tag: Tags.button;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['button'];
type AnchorProps = {
tag: Tags.a;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['a'];
type InputProps = {
tag: Tags.input;
} & ({ a?: string; b?: undefined } | { a?: undefined; b?: string }) &
JSX.IntrinsicElements['input'];
type Props = ButtonProps | AnchorProps | InputProps;
const Button: React.FC<Props> = ({ children, tag }) => {
if (tag === Tags.button) {
return <button>{children}</button>;
}
if (tag === Tags.a) {
return <a href="#">{children}</a>;
}
if (tag === Tags.input) {
return <input type="button" />;
}
return null;
};
// In this instance the `href` should create a TS error but doesn't...
<Button tag={Tags.button} href="#">Click me</Button>
// ... however this does
<Button tag={Tags.button} href="#" a="foo">Click me</Button>
这已经被剥离了一点,以便能够提出这个问题。关键是我正在尝试一个有区别的联合以及交叉类型。我正在尝试根据标签值实现所需的道具。因此,如果Tags.button
使用,则使用 JSX 的按钮属性(并且href
在上面的示例中应该创建一个错误,因为它不允许在button
元素上使用) - 但是另一个复杂性是我想要使用a
或b
使用,但它们不能一起使用 -因此交叉类型。
我在这里做错了什么,为什么在添加a
orb
属性时该类型只能按预期工作?
更新
我添加了一个带有示例的游乐场,以显示它何时应该出错以及何时应该编译。
解决方案
在您的示例中,有两个问题必须解决,并且都源于同一个“问题”(功能)。
在 Typescript 中,以下内容并不像我们有时想要的那样工作:
interface A {
a?: string;
}
interface B {
b?: string;
}
const x: A|B = {a: 'a', b: 'b'}; //works
您想要的是从 A 中明确排除 B,从 B 中排除 A - 这样它们就不能一起出现。
这个问题讨论了类型的“异或”,并建议使用包ts-xor,或者自己编写。这是那里的答案示例(在 ts-xor 中使用相同的代码):
type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
type XOR<T, U> = (T | U) extends object ? (Without<T, U> & U) | (Without<U, T> & T) : T | U;
现在,有了这个,我们终于可以解决你的问题了:
interface A {
a?: string;
}
interface B {
b?: string;
}
interface C {
c?: string;
}
type CombinationProps = XOR<XOR<A, B>, C>;
let c: CombinationProps;
c = {}
c = {a: 'a'}
c = {b: 'b'}
c = {c: 'c'}
c = {a: 'a', b: 'b'} // error
c = {b: 'b', c: 'c'} // error
c = {a: 'a', c: 'c'} // error
c = {a: 'a', b: 'b', c: 'c'} // error
更具体地说,您的类型将是:
interface A {a?: string;}
interface B {b?: string;}
type CombinationProps = XOR<A, B>;
type ButtonProps = {tag: Tags.button} & JSX.IntrinsicElements['button'];
type AnchorProps = {tag: Tags.a} & JSX.IntrinsicElements['a'];
type InputProps = {tag: Tags.input} & JSX.IntrinsicElements['input'];
type Props = CombinationProps & XOR<XOR<ButtonProps,AnchorProps>, InputProps>;
推荐阅读
- git - 基础分支未显示 UI 拉取请求中可用的所有分支
- django - Django 表单与 HTML 表单
- javascript - 打字稿:防止 React 道具对象的额外键?
- node.js - 无法连接到 Mongo Atlas
- c++ - VSCode 不编译 C++
- react-native - TypeError: _ExponentCameraManager__WEBPACK_IMPORTED_MODULE_13__ .default.requestPermissionsAsync 不是函数
- python - Tensorflow 卡在第一个 epoch
- java - 给定斐波那契数列中的数字总和
- java - Apache Camel 重新交付策略 Junit 测试
- c# - 从键盘键入时创建通用事件处理程序