【问题标题】:Typescript Conditionally required type using recursionTypescript 使用递归的有条件需要的类型
【发布时间】:2022-02-03 17:50:35
【问题描述】:

我整天都在为此苦苦挣扎,我是打字稿的新手,并试图在数组中存在特定值时有条件地要求对象属性。

这里是链接生成器。只有 Pinterest 关心图像 URL,所以如果“pinterest”在平台数组中,我想要求它。

type share = 'facebook' | 'pinterest' | 'twitter'

interface shareProps {
    pageTitle: string;
    platforms: share[];
    url: string;
    description: string;
}

interface propsWithImage extends shareProps {
    image: string;
}

type hasPinterest<T extends any[]> = T extends [infer U, ...infer V]
    ? U extends 'pinterest'
        ? propsWithImage
        : hasPinterest<V>
    : shareProps

function fn <T extends shareProps> (props: hasPinterest<T['platforms']>): string[] {
    const { platforms, url, description } = props
    const shareLinks: string[] = []
    platforms.forEach((platform) => {
        if (platform === 'pinterest') {
            shareLinks.push(`https://pinterest.com/pin/create/button/?url=${props.url}&media=${props.image}&description=${props.description}`)
        }
        if (platform === 'facebook') {
            shareLinks.push(`https://www.facebook.com/sharer/sharer.php?u=${props.url}`)
        }
        if (platform === 'twitter') {
            shareLinks.push(`https://twitter.com/intent/tweet?text=${props.description}`)
        }
    })
    return shareLinks
}

let shouldRequireImage = fn({
    pageTitle: 'Cool page',
    platforms: ['facebook', 'pinterest'],
    url: 'http://www.example.org',
    description: 'A really cool page',
    image: 'AwesomeImage.jpg'
})

let shouldNotRequireImage = fn({
    pageTitle: 'Cool page',
    platforms: ['facebook', 'twitter'],
    url: 'http://www.example.org',
    description: 'A really cool page',
})

console.log(shouldRequireImage)
console.log(shouldNotRequireImage)

在使用带有“平台”索引的泛型时,hasPinterest 似乎总是给出 shareProps 的类型。我错过了什么?

TS Playground

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    这里不需要递归。诀窍只是提取platforms 的值并确定——在TypeScript 中——它是否包含pinterest。为此,我使用了来自this post 的一个方便的小实用程序:

    type NeededUnionType<T extends any[]> = T[number];
    

    这让我们可以将platforms 数组转换为联合,然后我们只需确定pinterest 是否扩展了该联合。

    type HasPinterest<T extends Share[]> = 'pinterest' extends NeededUnionType<T> ? SharePropsWithImage<T> : SharePropsWithoutImage<T>
    

    然后我们只需要定义您的 props 类型,以便使用泛型类型轻松提取 platforms 的精确类型,提取它,并将其传递给辅助函数。

    这是完整的解决方案:

    type Share = 'facebook' | 'pinterest' | 'twitter'
    
    type SharePropsWithoutImage<T extends Share[]> = 
    {
        pageTitle: string;
        platforms: T;
        url: string;
        description: string;
      } 
    
    type SharePropsWithImage<T extends Share[]> = 
    { 
        pageTitle: string;
        platforms: T;
        url: string;
        description: string;
        image: string;
      }
    
    type ShareProps<T extends Share[]> = SharePropsWithImage<T> | SharePropsWithoutImage<T>
    
    type NeededUnionType<T extends any[]> = T[number];
    
    type HasPinterest<T extends Share[]> = 'pinterest' extends NeededUnionType<T> ? SharePropsWithImage<T> : SharePropsWithoutImage<T>
    
    function fn <T extends Share[]> (props:HasPinterest<T>): string[] {
        // the code...
    }
    

    playground

    如果您需要更多解释,请告诉我。

    【讨论】:

    • 很好,我必须记住数组到联合的转换技巧!感谢您的回答@sam256!
    猜你喜欢
    • 1970-01-01
    • 2018-05-30
    • 1970-01-01
    • 2020-10-18
    • 2019-02-10
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多