打字稿使用递归有条件地要求类型
Typescript Conditionally required type using recursion
我一整天都在为这个问题苦苦挣扎,我是打字稿的新手,当数组中存在特定值时,我试图有条件地需要一个对象属性。
这里是 link 生成器。只有 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 似乎总是在使用具有 'platform' 索引的泛型时给出 shareProps 的类型。我错过了什么?
这里不需要递归。诀窍就是提取 platforms
的值并确定——在 TypeScript 中——它是否包含 pinterest
。为此,我使用了 :
中的一个方便的小实用程序
type NeededUnionType<T extends any[]> = T[number];
这样我们就可以将 platforms
数组转换为联合,然后我们只需确定 pinterest
是否扩展了该联合。
type HasPinterest<T extends Share[]> = 'pinterest' extends NeededUnionType<T> ? SharePropsWithImage<T> : SharePropsWithoutImage<T>
然后我们只需要以一种允许使用通用类型轻松提取 platforms
的精确类型的方式定义您的 props
类型,提取它,并将其传递给辅助函数。
完整的解决方案如下:
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.
如果您需要更多解释,请告诉我。
我一整天都在为这个问题苦苦挣扎,我是打字稿的新手,当数组中存在特定值时,我试图有条件地需要一个对象属性。
这里是 link 生成器。只有 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 似乎总是在使用具有 'platform' 索引的泛型时给出 shareProps 的类型。我错过了什么?
这里不需要递归。诀窍就是提取 platforms
的值并确定——在 TypeScript 中——它是否包含 pinterest
。为此,我使用了
type NeededUnionType<T extends any[]> = T[number];
这样我们就可以将 platforms
数组转换为联合,然后我们只需确定 pinterest
是否扩展了该联合。
type HasPinterest<T extends Share[]> = 'pinterest' extends NeededUnionType<T> ? SharePropsWithImage<T> : SharePropsWithoutImage<T>
然后我们只需要以一种允许使用通用类型轻松提取 platforms
的精确类型的方式定义您的 props
类型,提取它,并将其传递给辅助函数。
完整的解决方案如下:
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.
如果您需要更多解释,请告诉我。