I am trying to get to the WANTED
type from the definition of columns
.
declare class ColumnDefinition<T, R extends boolean = false, N extends string = string> {
typeValidator: (v: unknown) => v is T;
required: R;
name: N;
};
type columns = [
ColumnDefinition<number, true, 'ID'>,
ColumnDefinition<string, true, 'text'>,
ColumnDefinition<boolean, false, 'bool'>,
ColumnDefinition<bigint, false, 'bigint'>,
];
type WANTED = {
ID: number;
text: string;
bool?: boolean | undefined;
biging?: bigint | undefined;
};
I tried to look for TupleFilter
methods, but they all seem to break when defining the recursive part.
type FilterColumns<
Required extends boolean,
COLUMNS extends readonly ColumnDefinition<any, boolean, any>[],
> = COLUMNS extends readonly [infer L, ...infer Rest]
? L extends ColumnDefinition<any, Required>
? [L, ...FilterColumns<Required, Rest>]
: FilterColumns<Required, Rest>
: [];
In the code above specifically FilterColumns<Required, Rest>
the Rest
is highlighted and tells that unknown[]
is not assignable to readonly ColumnDefinition<any, boolean, any>[]
.
Why is Rest
defined as unknown[]
instead of ColumnDefinition<number, boolean, any>[]
? How to fix this?
You've run into the issue reported at microsoft/TypeScript#45281. When using conditional types to infer
the rest element of a variadic tuple type, TypeScript doesn't re-constrain the resulting array type. It essentially falls back to unknown[]
, as you've seen.
This was considered to be a bug when it was reported. TypeScript 4.7 introduced support for extends
constraints on infer
type variables, meaning that you can manually set the constraint yourself. Your code example therefore becomes
type FilterColumns<
Required extends boolean,
COLUMNS extends readonly ColumnDefinition<any, boolean, any>[],
> = COLUMNS extends readonly [infer L,
...infer Rest extends readonly ColumnDefinition<any, boolean, any>[]]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
? L extends ColumnDefinition<any, Required>
? [L, ...FilterColumns<Required, Rest>]
: FilterColumns<Required, Rest>
: [];
and it works as expected.
The bug is on the Backlog, which generally means that pull requests from community members are welcomed. If you really want to see this fixed, you might want to fix it yourself and submit a pull request. Currently the issue only has a few upvotes and it can be worked around with an extends
constraint, so it's not very likely that the TS team will decide to fix it directly.