Reputation: 35493
I have a function which I want it to return 2 type different types based on its argument props.
interface IPaginateParams {
perPage: number;
currentPage: number;
isFromStart?: boolean;
}
interface IWithPagination<Data, TParams extends IPaginateParams = IPaginateParams> {
data: Data;
pagination: IPagination<TParams>;
}
type IPagination<TParams> = TParams extends
| { currentPage: 1 }
| { isFromStart: true }
| { isLengthAware: true }
? ILengthAwarePagination
: IBasePagination;
interface IBasePagination {
currentPage: number;
perPage: number;
from: number;
to: number;
}
interface ILengthAwarePagination extends IBasePagination {
total: number;
lastPage: number;
}
function paginate<TData = any[], TParams extends IPaginateParams = IPaginateParams>(
options: TParams
): IWithPagination<TData, TParams>;
The idea is that if you pass currentPage: 1
or isFromStart: true
, it should add 2 additional types to the pagination
object.
The weird thing is that IWithPagination
works as expected,
const data = {} as IWithPagination<any, {perPage: 2, currentPage: 1}>;
expectType<ILengthAwarePagination>(data.pagination);
But when I use the invocation, it always return the IBasePagination
const data = paginate({perPage: 2, currentPage: 1});
expectType<ILengthAwarePagination>(data.pagination) // fails
// or
const data = paginate({perPage: 2, currentPage: 2, isFromStart: true});
expectType<ILengthAwarePagination>(data.pagination) // fails
Upvotes: 2
Views: 339
Reputation: 35493
As @OlegValter explained in the comments, when passing an object to paginate
it inferred as a widen type, for example:
{perPage: 2, currentPage: 2, isFromStart: true} // inferred as {perPage: number; currentPage: number; isFromStart: boolean}
Therefore, the check in the return type always fallback to the IBasePagination
type (the else clause).
All we need to do, is to specify that the arguments of the function are readonly
.
declare function paginate<TData = any[], TParams extends IPaginateParams = IPaginateParams>(
options: Readonly<TParams>
// ---------^ this is what made the input as a narrow type
): IWithPagination<TData, TParams>;
Working example
Upvotes: 1