To ensure proper functionality, it is recommended to adjust the constraint on V
and switch from any[]
to a readonly
array type, such as readonly any[]
:
function insertIf<const V extends readonly any[]>(
condition: boolean, ...value: V
): V | readonly [] {
if (condition) { return value; } else { return []; }
}
const x = insertIf(true, { s: 'a' }, { s: '?' });
// const x: readonly [] | readonly [{ readonly s: "a"; }, { readonly s: "?"; }]
In regards to the explanation of microsoft/TypeScript#51865, pertaining to the implementation of const
type parameters, it mentions:
If a const
type parameter is restricted to an array type, it is essential for that array type to include a readonly
modifier; otherwise, inferences for the type parameter will not meet the constraint.
Therefore, with your original version:
function insertIf<const V extends any[]>(
condition: boolean, ...value: V
): V | readonly [] {
if (condition) { return value; } else { return []; }
}
const x = insertIf(true, { s: 'a' }, { s: '?' });
// const x: any[] | readonly []
The compiler actually seeks to infer V
as
readonly [{ readonly s: "a"; }, { readonly s: "?"; }]
here, akin to utilizing a
const
assertion. However, this type is not compatible with
any[]
; readonly arrays have fewer known methods compared to normal read-write arrays.
Consequently, the inference process fails, leading the compiler to revert to the constraint of any[]
, which passes the type check and may result in seemingly silent code failure.
This has been acknowledged as a source of confusion and has previously been reported as a bug, as seen in microsoft/TypeScript#51931. It would be beneficial if the compiler could provide some form of warning indicating that the const
modifier might not function optimally, but as of now, the behavior aligns with its intended design, with the solution being to incorporate the readonly
modifier.
Playground link to code