DEV Community

Cover image for 1-10 Сustom Utility Types for TypeScript Projects

1-10 Сustom Utility Types for TypeScript Projects

Anton Zamay on March 31, 2024

In the dynamic landscape of TypeScript development, utility types stand as base tools for crafting adaptable, clear, and robust type arrangements. ...
Collapse
 
bcostaaa01 profile image
Bruno

Quite handy stuff for Typescript! The JSONObject caught my attention particularly, might come handy in form submitting scenarios, for example. Nice read 👍

Collapse
 
coolcucumbercat profile image
coolCucumber-cat

Instead of NonNullableKeys, you should create a type that lets you pass in any type instead of Nullish, and then if you want non nullable keys, you just pass in Nullish. That way you can also reuse it for other types, not just Nullish.

Collapse
 
antonzo profile image
Anton Zamay

Good suggestion! I will provide corresponding types if you mentioned the more general solution anyways. These types are basically extensions of Pick and Omit, but with the Condition:

type FilterKeys<T, Condition> = {
  [K in keyof T]: K extends Condition ? K : never
}[keyof T];

type PickByCondition<T, Condition> = Pick<T, FilterKeys<T, Condition>>;
type OmitByCondition<T, Condition> = Omit<T, FilterKeys<T, Condition>>;
Enter fullscreen mode Exit fullscreen mode
Collapse
 
nickhodges profile image
Nick Hodges

I love this article so much.

Collapse
 
filipdanic profile image
Filip Danić • Edited

Really, nice article, but the ReadOnlyDeep utility needs some more work. It does not cover arrays.

Example:

Collapse
 
antonzo profile image
Anton Zamay

Hey, thanks for your comment!

If a property's type is an object (which includes arrays, as arrays are objects in JavaScript), the type recursively applies the ReadonlyDeep transformation to that object. So it should work.

Try ReadonlyDeep instead of Readonly in your example.

Collapse
 
filipdanic profile image
Filip Danić

Ooops, you are completely right of course! I was comparing out a few different things in the playground and didn’t notice I made the switch. I was really confused too since I know I saw you used a recursive type – for a moment I thought this was a TS bug!

Thread Thread
 
antonzo profile image
Anton Zamay

That's ok, always happens with me too :D

Collapse
 
tmish profile image
Timur Mishagin

Great article! Thanks!

Collapse
 
excalibaard profile image
Excalibaard • Edited

The NonNullableKeys example doesn't work for me (TS playground, 5.6.3 with strictNullChecks enabled). Optional keys and keys with nullish values were still valid within the type. I had more success by comparing T[K] and whether it fits inside its NonNullable variant.

type NonNullableKeys<T> = {
  [K in keyof T]: T[K] extends NonNullable<T[K]> ? K : never
}[keyof T];
Enter fullscreen mode Exit fullscreen mode

Works very nicely in OptionalExceptFor<T, NonNullableKeys<T>>.

Collapse
 
antonzo profile image
Anton Zamay

The only issue I discovered is related to optional keys (test case 4 doesn't pass). However, it's easy to fix by removing the optional modifiers from the properties using the -? operator in the mapped type. This ensures that T[K] reflects the declared type without the implicit undefined:

type NonNullableKeys<T> = {
  [K in keyof T]-?: T[K] extends Nullish ? never : K;
}[keyof T];
Enter fullscreen mode Exit fullscreen mode

typescriptlang.org/play/?#code/C4T...

Collapse
 
excalibaard profile image
Excalibaard • Edited

Ah, I misunderstood the use case:

I was looking for 'exclude all keys that contain Nullish', while your implementation works by 'include all keys that contain not Nullish'.
E.g. key1: string | undefined should be rejected in my implementation, while it should be accepted in yours.

Thanks for sharing the playground and the article, it still helped me to get to the solution I needed!

P.S. If exactOptionalPropertyTypes is set to true in tsconfig, this also prevents the implicit undefined, without stripping the optionality away. This in turn is really great make a 'minimum template' for an object in combination with the OptionalExceptFor helper: always define the keys that may not be nullish, and if you define more, they may not have the value null or undefined either.