TypeScript:根据输入数组元素推断 return 数组类型
TypeScript: infer return type of array based on input array elements
我有一个函数可以有多个 Node
或 Relationship
或 undefined
类型的参数。它具有以下签名:
export function useFormat(...elements: Array<Node | Relationship | undefined>) {
const formattedElements: Array<FormattedNode | FormattedRelationship | undefined> = [];
// do formatting...
return formattedElements;
}
我将其称为 React Hook,并根据给定的元素类型进行一些格式化。然后我 return 一个具有基本相同元素但格式不同的新数组:Array<FormattedNode | FormattedRelationship | undefined>
.
我想知道 TypeScript 是否可以根据原始数组 elements
.[=21] 中的相同元素推断 returned 数组中每个元素的类型=]
例如:原始数组 elements[0]
中的第一个元素是 Node
,因此 returned 数组 formattedElements[0]
的第一个元素将是 FormattedNode
.
我相信你应该在这里使用重载:
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type Return<T extends Input> =
T extends CustomNode
? 0
: T extends Relationship
? 1
: T extends undefined
? 2
: 3
function useFormat<
T extends Input,
U extends T[],
R extends {
0: FormattedNode[],
1: FormattedRelationship[],
2: undefined[],
3: never
}[Return<T>]>(...elements: [T, ...U]): R
function useFormat<
T extends Input,
U extends T[],
>(...elements: [T, ...U]) {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }) // FormattedNode
可读性强的格式:
function useFormat<
T extends CustomNode,
U extends T[]>(...elements: [T, ...U]): FormattedNode[]
function useFormat<
T extends Relationship,
U extends T[]>(...elements: [T, ...U]): FormattedRelationship[]
function useFormat<
T extends undefined,
U extends T[]>(...elements: [T, ...U]): undefined[]
function useFormat<
T extends Input,
U extends T[],
>(...elements: [T, ...U]) {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }) // FormattedNode
更新
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type MapPredicate<T> =
T extends Input
? T extends CustomNode
? FormattedNode
: T extends Relationship
? FormattedRelationship
: T extends undefined
? undefined
: never : never
type Mapped<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MapPredicate<H>]
: Arr extends [infer Head, ...infer Tail]
? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
: Readonly<Result>;
function useFormat<
T extends Input,
U extends T[],
>(...elements: [...U]): Mapped<U>
function useFormat<
T extends Input,
U extends T[],
>(...elements: [...U]): any {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }, { type: 'Relationship' }) // FormattedNode
请记住,这些类型不是 100% 安全的,因为我使用了 any
以使其与重载兼容。
其次,最好定义1个以上的重载。由于数组类型是可变的,有时很难在不使用类型断言或 any
.
的情况下编写类型安全的函数
现在,你知道所有的缺点了。
如果你想了解更多关于文字数组映射或元组的知识,你可以参考我的blog
更新 - 没有休息
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type MapPredicate<T> =
T extends Input
? T extends CustomNode
? FormattedNode
: T extends Relationship
? FormattedRelationship
: T extends undefined
? undefined
: never : never
type Mapped<
Arr extends ReadonlyArray<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MapPredicate<H>]
: Arr extends [infer Head, ...infer Tail]
? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
: Readonly<Result>;
function useFormat<
T extends Input,
U extends ReadonlyArray<T>,
>(elements: [...U]): Mapped<U>
function useFormat<
T extends Input,
U extends T[],
>(elements: U): any {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat([{ type: 'Relationship' }]) // FormattedRelationship
const result2 = useFormat([{ type: 'CustomNode ' }, { type: 'Relationship' }]) // FormattedNode
当您需要 [...U]
而不是 U
时就是这种情况。它有助于推断数组中的每个元素。尝试在 overload
中用 U
替换 [...U]
我有一个函数可以有多个 Node
或 Relationship
或 undefined
类型的参数。它具有以下签名:
export function useFormat(...elements: Array<Node | Relationship | undefined>) {
const formattedElements: Array<FormattedNode | FormattedRelationship | undefined> = [];
// do formatting...
return formattedElements;
}
我将其称为 React Hook,并根据给定的元素类型进行一些格式化。然后我 return 一个具有基本相同元素但格式不同的新数组:Array<FormattedNode | FormattedRelationship | undefined>
.
我想知道 TypeScript 是否可以根据原始数组 elements
.[=21] 中的相同元素推断 returned 数组中每个元素的类型=]
例如:原始数组 elements[0]
中的第一个元素是 Node
,因此 returned 数组 formattedElements[0]
的第一个元素将是 FormattedNode
.
我相信你应该在这里使用重载:
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type Return<T extends Input> =
T extends CustomNode
? 0
: T extends Relationship
? 1
: T extends undefined
? 2
: 3
function useFormat<
T extends Input,
U extends T[],
R extends {
0: FormattedNode[],
1: FormattedRelationship[],
2: undefined[],
3: never
}[Return<T>]>(...elements: [T, ...U]): R
function useFormat<
T extends Input,
U extends T[],
>(...elements: [T, ...U]) {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }) // FormattedNode
可读性强的格式:
function useFormat<
T extends CustomNode,
U extends T[]>(...elements: [T, ...U]): FormattedNode[]
function useFormat<
T extends Relationship,
U extends T[]>(...elements: [T, ...U]): FormattedRelationship[]
function useFormat<
T extends undefined,
U extends T[]>(...elements: [T, ...U]): undefined[]
function useFormat<
T extends Input,
U extends T[],
>(...elements: [T, ...U]) {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }) // FormattedNode
更新
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type MapPredicate<T> =
T extends Input
? T extends CustomNode
? FormattedNode
: T extends Relationship
? FormattedRelationship
: T extends undefined
? undefined
: never : never
type Mapped<
Arr extends Array<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MapPredicate<H>]
: Arr extends [infer Head, ...infer Tail]
? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
: Readonly<Result>;
function useFormat<
T extends Input,
U extends T[],
>(...elements: [...U]): Mapped<U>
function useFormat<
T extends Input,
U extends T[],
>(...elements: [...U]): any {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat({ type: 'Relationship' }) // FormattedRelationship
const result2 = useFormat({ type: 'CustomNode ' }, { type: 'Relationship' }) // FormattedNode
请记住,这些类型不是 100% 安全的,因为我使用了 any
以使其与重载兼容。
其次,最好定义1个以上的重载。由于数组类型是可变的,有时很难在不使用类型断言或 any
.
现在,你知道所有的缺点了。
如果你想了解更多关于文字数组映射或元组的知识,你可以参考我的blog
更新 - 没有休息
type Relationship = {
type: 'Relationship'
}
type FormattedRelationship = {
type: 'FormattedRelationship'
}
type CustomNode = {
type: 'CustomNode '
}
type FormattedNode = {
type: 'FormattedNode'
}
type Input = CustomNode | Relationship | undefined
type Output = FormattedNode | FormattedRelationship | undefined
type MapPredicate<T> =
T extends Input
? T extends CustomNode
? FormattedNode
: T extends Relationship
? FormattedRelationship
: T extends undefined
? undefined
: never : never
type Mapped<
Arr extends ReadonlyArray<unknown>,
Result extends Array<unknown> = []
> = Arr extends []
? []
: Arr extends [infer H]
? [...Result, MapPredicate<H>]
: Arr extends [infer Head, ...infer Tail]
? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
: Readonly<Result>;
function useFormat<
T extends Input,
U extends ReadonlyArray<T>,
>(elements: [...U]): Mapped<U>
function useFormat<
T extends Input,
U extends T[],
>(elements: U): any {
const formattedElements: Output[] = [];
// do formatting...
return formattedElements
}
const result = useFormat([{ type: 'Relationship' }]) // FormattedRelationship
const result2 = useFormat([{ type: 'CustomNode ' }, { type: 'Relationship' }]) // FormattedNode
当您需要 [...U]
而不是 U
时就是这种情况。它有助于推断数组中的每个元素。尝试在 overload
U
替换 [...U]