首页 > 解决方案 > TypeScript 使用泛型有条件地合并道具类型

问题描述

编译器告诉我type 上不存在propertiespropApropBinside 。我对条件类型有什么遗漏或误解吗?TestComponentProps<T>

import React from 'react';

type PropsBase<T extends boolean | undefined> = {
  isA?: T;
};

type PropsA = {
  propA: string;
};

type PropsB = {
  propB: string;
};

type Props<T extends boolean | undefined> = PropsBase<T> & (T extends false | undefined ? PropsB : PropsA);

function TestComponent<T extends boolean | undefined = true>(props: Props<T>) {
  if (props.isA) {
    return <>{props.propA}</>; // Property 'propA' does not exist
  }
  if (!props.isA) {
    return <>{props.propB}</>; // Property 'propB' does not exist
  }

  return <></>;
}

<>
  <TestComponent propA="propA" /> // Should be valid
  <TestComponent isA propA="propA" /> // Should be valid
  <TestComponent isA={false} propB="propB" /> // Should be valid
  <TestComponent isA propB="propB" /> // Should be invalid
</>

我的目标是创建一个可扩展和可重用的类型,其属性可以通过泛型来控制。我知道这也可以通过联合来完成,但是在它之上构建其他类型并不容易。

游乐场链接

标签: typescripttypescript-genericsreact-typescript

解决方案


我认为您的第一个示例<TestComponent propA="propA" />也应该无效?毕竟,在那种情况下,ìsA is of typeundefined`。

我把你Props<T>改成了这个Props

type Props = ({ isA: true } & PropsA) | ({ isA?: false } & PropsB);

function TestComponent(props: Props) {
    if (props.isA) {
        return props.propA; // OK
    }
    if (!props.isA) {
        return props.propB; // OK
    }
}

您对它的使用现在也得到了正确验证:

TestComponent({ propA: 'propA' }); // Property 'isA' is missing
TestComponent({ propB: 'propB' }); // OK
TestComponent({ isA: true, propA: 'propA' }); // OK
TestComponent({ isA: false, propB: 'propB' }); // OK
TestComponent({ isA: true, propB: 'propB' }); // 'propB' does not exist

有趣的是,如果我使用prop.isA === true,TypeScript 的表现会更好:

function TestComponent(props: Props) {
    if (props.isA === true) {
        return props.propA; // OK
    }
    const isA = props.isA; // type `false | undefined`
    if (!props.isA) {
        return props.propB; // OK
    }
}

我希望在你的情况下这应该足够好了吗?尽管我对 和 之间的区别感到if (props.isA)惊讶if (props.isA === true)。请注意,这需要 TypeScript 在严格模式下运行,并且noImplicitAny启用是特定的。否则你需要使用if (props.isA === true). 很奇怪,但noImplicitAny无论如何,在禁用的情况下运行是非常罕见的。


推荐阅读