DEV Community

Pragmatic Maciej
Pragmatic Maciej

Posted on

14 6

Advanced TypeScript Exercises - Question 5

We have function getUser which gets Config object, the object defines what fields of User function will return. If for example config says { name: true, lastname: false } it means returned object should have name field non-optional but no field lastname. Current User type is very broad type of the return, we need to narrow it down depending on the config passed as argument of getUser. Solution should be done only at the type level, no value level code should be written. Only function declaration getUser is to be changed.

// Here types should remain the same âť„
type Config = {
  name: boolean;
  lastname: boolean;
};
type User = {
  name?: string;
  lastname?: string;
};

// Here declaration to be changed 🔥
declare function getUser(
     config: Config
): User;

// test cases
const user = getUser({ name: true, lastname: false })
user.name // this field should be non-optional
user.lastname // this field should not be there and we should have compile error 🛑

const user2 = getUser({ name: true, lastname: true })
user2.name // this field should be non-optional
user2.lastname // this field should be non-optional

const user3 = getUser({ name: false, lastname: true })
user3.name // this field should not be there and we should have compile error 🛑
user3.lastname // this field should be non-optional

const user4 = getUser({ name: false, lastname: false })
user4 // user4 should be empty object {}
Enter fullscreen mode Exit fullscreen mode

Full code can be found in the playground.

Post your answers in comments. Have fun! Answer will be published soon!

This series is just starting. If you want to know about new exciting questions from advanced TypeScript please follow me on dev.to and twitter.

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (6)

Collapse
 
kashyaprahul94 profile image
Rahul Kashyap •

Quick one -


type KeysOfTrueValues<T extends Config> = Pick<T, {
    [K in keyof T]: T[K] extends true ? K : never
}[keyof T]>;

type UserReturnType<T extends User, C extends Config> = Required<Omit<T, Exclude<keyof T, keyof KeysOfTrueValues<C>>>>;

// Here declaration to be changed 🔥
declare function getUser<T extends User, C extends Config>(
     config: C
): UserReturnType<T, C>;

Enter fullscreen mode Exit fullscreen mode

Playground link

Collapse
 
alextsk profile image
alextsk • • Edited

tried to simplify your solution

type OptionalFields<T extends Config> = Pick<T,{
  [K in keyof T]: T[K] extends false ? K : never    
}[keyof T]>

type Result<C extends Config> = Required<Omit<User, keyof OptionalFields<C>>>

declare function getUser<C extends Config>(
     config: C
): Result<C>;
Enter fullscreen mode Exit fullscreen mode

--edit
one more minor improvement

type OptionalFields<T extends Config> = {
  [K in keyof T]: T[K] extends false ? K : never    
}[keyof T]

type Result<C extends Config> = Required<Omit<User, OptionalFields<C>>>

Enter fullscreen mode Exit fullscreen mode
Collapse
 
kashyaprahul94 profile image
Rahul Kashyap • • Edited

Slightly verbose one -

type KeysOfTrueValues<T extends Config> = Pick<T, {
    [K in keyof T]: T[K] extends true ? K : never
}[keyof T]>;

type OnlyTrueKeys<T extends User, C extends Config> = Exclude<keyof T, keyof KeysOfTrueValues<C>>;

type FilteredUser<T extends User, C extends Config> = Omit<T, OnlyTrueKeys<T, C>>;

type FilteredUserRequiredFields<T extends User, C extends Config> = Required<FilteredUser<T, C>>;

type UserReturnType<T extends User, C extends Config> = FilteredUserRequiredFields<T, C>;


// Here declaration to be changed 🔥
declare function getUser<T extends User, C extends Config>(
     config: C
): UserReturnType<T, C>;
Enter fullscreen mode Exit fullscreen mode

Playground Link

Collapse
 
dwjohnston profile image
David Johnston •

Here's a simpler solution than has been posted:

// Here declaration to be changed 🔥
declare function getUser<T extends Config>(
  config: T
): {
  name: T["name"] extends true ? string : never,
  lastname: T["lastname"] extends true ? string : never,
}; 
Enter fullscreen mode Exit fullscreen mode

Now in this case we're not getting compile errors - however the types of those values are never so depending on your use case - might be enough to prevent errors from occuring.

Collapse
 
macsikora profile image
Pragmatic Maciej •

Hi David, again please pay attention I put 🛑 icon where the code should show compile error. Your implementation doesn't create such. So its not valid solution.

Collapse
 
drazbest profile image
dr-azbest • • Edited
type UserByConfig<T extends Config> = {
  [K in keyof User as T[K] extends true ? K : never] -?: User[K]
} & {}
Enter fullscreen mode Exit fullscreen mode

Playground Link

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post

Best practices for optimal infrastructure performance with Magento

Running a Magento store? Struggling with performance bottlenecks? Join us and get actionable insights and real-world strategies to keep your store fast and reliable.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️