TypeScript doesn't currently give strong types for relative indexing into tuple types.
There is such a thing as leading rest elements in tuple types like [...unknown[], string, number[]]
, but this just makes it easy to represent a supertype of T1 | T2 | T3
; it doesn't give you strong typings for arg[arg.length - 2]
. It's more helpful to callers of your function than to the implementation.
您不能从arg[arg.length - 2]
获得强类型,因为arg.length - 2
的类型只有number
;没有内置的对数字literal types的类型级别操作的支持,正如长期公开的问题microsoft/TypeScript#26382中所要求的那样.即使有一天这种变化,T1 | T2 | T3
是union仍然可能做坏事,因为arg.length - 2
将被判断为1 | 2 | 3
,这将与arg
的类型无关(参见ms/TS#30581).因此,这可能是一条死胡同.
在microsoft/TypeScript#47660有一个功能请求,要求在调用带有负指数的the at()
array method时给出强类型,但被拒绝了,理由是"太复杂".
虽然您可以自己使用try来完成此操作,但由于缺乏对数字文字类型算术的支持,您必须跳过一些难看的圈套,如in this comment所示:
type Reverse<T extends readonly any[], U extends any[] = []> =
T extends readonly [infer F, ...infer R] ? Reverse<R, [F, ...U]> : U
type At<T extends readonly any[], I extends number> =
`${I}` extends `-${infer J}` ? [never, ...Reverse<T>] extends
infer R ? J extends keyof R ? R[J] :
undefined : never : T[I]
interface ReadonlyArray<T> {
at<I extends number>(index: I): At<this, I>;
}
interface Array<T> {
at<I extends number>(index: I): At<this, I>;
}
它适用于您的示例代码:
function f(arg: T1 | T2 | T3): [string, number[]] {
const penultimate = arg.at(-2) // string
const ultimate = arg.at(-1) // number[]
return [penultimate, ultimate]; // okay
}
但这可能比它的价值更麻烦,特别是因为毫无疑问有很多边缘情况,你必须自己维护它.如果TS团队认为它太复杂,无法在原生TS库中提供,您可能应该避免为您自己的代码库这样做,除非您有非常好的理由.
因此,目前还不能用Typescript ,至少目前是这样.你不妨使用type assertions,就像arg[arg.length-2] as string
和arg[arg.length-1] as number[]
一样,然后继续前进.
Playground link to code个