【问题标题】:Optional generic type and corresponding parameters for TypeScriptTypeScript 的可选泛型类型和对应参数
【发布时间】:2021-04-26 09:12:52
【问题描述】:

我有以下采用泛型参数的 React 功能组件:

type Props<T, S> = {
  data: T[]
  additionalData: S
}

function Component<T, S = void>({
 ....
 

您可以按如下方式使用它:

<Component<Bike, BikeShed> data={bikes} additionalData={bikeShed} />

由于第二个泛型参数是可选的,所以你必须按如下方式使用它:

<Component<Car> data={cars} additionalData={undefined} />

现在问题来了。有没有办法省略additionalData={undefined},因为这里显然没有使用它。

我尝试的是这样的:

type Props<T, S = void> = {
  data: T[]
  additionalData?: S
}

但它实际上将S 更改为S | undefined,不是一个理想的情况!

当可选的泛型参数为void 时,我是否可以不必指定其相关属性来设置我的Props

【问题讨论】:

    标签: reactjs typescript generics


    【解决方案1】:

    你的Props 类型应该是严格联合类型。

    import React, { FC } from 'react'
    
    // credits goes https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
    type UnionKeys<T> = T extends T ? keyof T : never;
    type StrictUnionHelper<T, TAll> =
        T extends any
        ? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
    
    type StrictUnion<T> = StrictUnionHelper<T, T>
    
    type Props<T, S> = StrictUnion<{
        data: T[]
    } | {
        data: T[]
        additionalData: S
    }>
    
    type Bike = {
        color: string
    }
    
    type BikeShed = {
        model: string
    }
    const Foo = <T, S = void>(props: Props<T, S>) => {
        const { additionalData } = props // ok, but may be undefined
        const { data } = props; // ok
    
        return null
    }
    
    const bikes: Bike[] = []
    const bikeShed = { model: '' }
    
    const component = <Foo<Bike, BikeShed> data={[]} />
    

    Playground

    更多关于在 React 中处理 union props 的信息你可以在我的blog找到

    更新

    你也可以试试typeguards:

    import React, { FC } from 'react'
    
    type WithoutAdditional<T> = {
        data: T[]
    }
    type WithAdditional<T, S> = {
        data: T[]
        additionalData: S
    }
    
    type Props<T, S> =
        | WithAdditional<T, S>
        | WithoutAdditional<T>
    
    const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop): obj is Obj & Record<Prop, unknown> =>
        Object.prototype.hasOwnProperty.call(obj, prop)
    
    const withAdditional = <T, S>(obj: Props<T, S>): obj is WithAdditional<T, S> => hasProperty(obj, 'additionalData')
    
    type Bike = {
        color: string
    }
    
    type BikeShed = {
        model: string
    }
    const Foo = <T, S = void>(props: Props<T, S>) => {
        if (withAdditional(props)) {
            const x = props; // WIthAdditional
    
        } else {
            const y = props; // WIthoutAdditional
        }
    
        return null
    }
    
    const bikes: Bike[] = []
    const bikeShed = { model: '' }
    
    const component = <Foo<Bike, BikeShed> data={[]} />
    

    Playground 2

    【讨论】:

    • 使用这个和使additionalData可选(additionalData?: S)有什么区别?
    • 另外,我实际上有 2 种可选的泛型类型。如何在不增加类型排列数量的情况下做到这一点?
    • 只需将其全部设为可选并使用类型保护来处理它
    • @Owl 如果您已经有联合类型并且必须处理它,则上述方法会有所帮助。但总的来说,你是对的,我做了更新
    【解决方案2】:

    是的 - 您可以检查 S 是否为 void,如果是,则使用条件类型评估为没有附加数据的类型:

    type BaseProps<T> = { data: T[] };
    type PropsWithAdditionalData<T, S> = BaseProps<T> & { additionalData: S };
    type Props<T, S = void> = S extends void ? BaseProps<T> : PropsWithAdditionalData<T, S>;
    
    const hasAdditionalData = <T, S>(props: BaseProps<T> | PropsWithAdditionalData<T, S>): props is PropsWithAdditionalData<T, S> => {
      return !!(props as unknown as PropsWithAdditionalData<T, S>).additionalData;
    }
    
    function Component<T, S = void>(props: Props<T, S>) {
      if (hasAdditionalData<T, S>(props)) {
        return <div>{props.additionalData}</div>;
      }
      return <div>{props.data}</div>;
    }
    
    type Car = { id: number };
    const cars = [{ id: 1 }, { id: 2 }];
    
    const noAdditionalData = <Component<Car> data={cars} />;
    const additionalData = <Component<Car, number> data={cars} additionalData={1} />;
    
    

    S extends void 检查类型 S 是否可分配给 void(这是在默认情况下),如果是,则仅返回 { data: T[] }

    【讨论】:

    • 如果您将Props 分配为像function Comp&lt;T, S = void&gt;(props: Props&lt;T, S&gt;) { ... } 这样的组件道具类型,则无法访问additionalData
    • 是的 - 因为 TS 无法判断您将拥有两种类型中的哪一种,因此它可以安全地评估交叉点。你可以使用类型守卫告诉 TS 你有哪种类型
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-10
    • 2016-12-05
    • 2021-07-10
    • 2021-08-03
    • 2018-09-15
    • 2019-05-30
    相关资源
    最近更新 更多