1. Conditional types solution
type Config = {
name: boolean;
lastname: boolean;
};
type User = {
name?: string;
lastname?: string;
};
declare function getUser<
C extends Config,
_NamePart = C['name'] extends true ? Pick<Required<User>, 'name'> : {},
_LastNamePart = C['lastname'] extends true ? Pick<Required<User>, 'lastname'> : {}
>(
config: C
): _NamePart & _LastNamePart;
Check the solution in the playground.
Explanation
- declaration have generic type
C extends Config
in order to be able to work with narrowed variable type - we have created two local variables
_NamePart
and_LastNamePart
, the purpose is readability -
_NamePart = C['name'] extends true ? Pick<Required<User>, 'name'> : {}
- if type variableC
hasname
property set attrue
we assign type which has required fieldname
from the original typeUser
-
_LastNamePart = C['lastname'] extends true ? Pick<Required<User>, 'lastname'> : {}
- in the same way as before we ask aboutlastname
-
_NamePart & _LastNamePart
we return intersection type, it means depends on what we get in particular parts we join those parts.
Take a look that I have used monoid
property as in both conditional types I just fallback to {}
as neutral element of &
operation.
2. Overloads solution
type Config = {
name: boolean;
lastname: boolean;
};
type User = {
name?: string;
lastname?: string;
};
declare function getUser(
config: { name: true; lastname: false}
): Pick<Required<User>,'name'>;
declare function getUser(
config: { name: false; lastname: true}
): Pick<Required<User>,'lastname'>;
declare function getUser(
config: { name: false; lastname: false}
): {};
declare function getUser(
config: { name: true; lastname: true}
): Required<User>;
Check the solution in the playground
The solution with overloads is less sophisticated but also longer, in order to achieve the result we need to create overload for every possible correlation of both fields in Config
, so exactly 4 versions.
-
Pick<Required<User>,'name'>
we pick onlyname
field fromUser
and say its required. -
Required<User>
- we say we getUser
but with all fields non-optional
Utility types used in both solutions:
- Pick - allows for picking only wanted set of properties
- Required - produce a type with all properties required
Also want to mention more sophisticated solution by Rahul Kashyap left in the comment - Solution from the comments section. Good job Rahul!
This series is will continue. If you want to know about new exciting questions from advanced TypeScript please follow me on dev.to and twitter.
Top comments (3)
Here is a generic solution that will support future interface changes:
Here is an even more generic solution to any type (not just User):
Another solution: