【问题标题】:TypeScript Conditionally Merge Prop Types Using GenericsTypeScript 使用泛型有条件地合并道具类型
【发布时间】:2021-10-03 12:16:25
【问题描述】:

编译器告诉我TestComponent 内的属性propApropB 不存在于类型Props<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
</>

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

Playground link.

【问题讨论】:

    标签: typescript typescript-generics react-typescript


    【解决方案1】:

    我认为您的&lt;TestComponent propA="propA" /&gt; 的第一个示例也应该无效?毕竟,在那种情况下,ìsAis of typeundefined`。

    我将您的 Props&lt;T&gt; 改为此 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 的情况下运行是非常罕见的。

    【讨论】:

    • 感谢您的回答。 &lt;TestComponent propA="propA" /&gt; 有效,因为默认情况下它在 &lt;T extends boolean | undefined = true&gt; 中是 true。实际上它正确地验证了组件道具,问题是它们在函数内部没有被识别。我知道它适用于工会,但我特别感兴趣是否可以完成泛型。
    • @eugenics 啊,我错过了= true 部分。公平地说,即使默认情况下泛型类型是trueprops.isA 也不会。不确定是否真的可以使用泛型。 也许有重载的功能,但不要太有希望。
    猜你喜欢
    • 2020-04-14
    • 1970-01-01
    • 2021-06-29
    • 2020-12-14
    • 1970-01-01
    • 2021-12-15
    • 1970-01-01
    • 2018-03-05
    • 2016-06-11
    相关资源
    最近更新 更多