Build GetOptional<T>, which keeps only the optional fields of an object type. Filtering keys is half the job; each surviving property must also keep its ? modifier.
GetOptional<T> keeps every optional field of an object type and drops the rest. Picking the right keys is only half the job. The result must also keep the ? modifier on every surviving property, so bar?: string stays bar?: string. The solution combines the {} extends Pick<T, K> optionality probe with as-remapped key filtering, and homomorphic mapped types hand you the modifiers for free.
For example
[object Object]Implement the advanced util type GetOptional<T>, which remains all the optional fields
For example
[object Object]View on GitHub: https://tsch.js.org/59
Change the following code to make the test cases pass (no type check errors).
The whole solution is one mapped type:
type GetOptional<T> = {
[K in keyof T as {} extends Pick<T, K> ? K : never]: T[K]
}It's the mirror image of GetRequired<T>: same probe, swapped branches.
The ? modifier isn't visible in a property's value type, so the second test case defeats any undefined-based check:
type T = { foo: undefined; bar?: undefined }
// GetOptional<T> must be { bar?: undefined }Instead, isolate each property with Pick, which preserves modifiers, and test assignability of the empty object:
// Pick<T, 'foo'> = { foo: undefined } → {} extends it? false (foo is mandatory)
// Pick<T, 'bar'> = { bar?: undefined } → {} extends it? true (bar may be absent){} extends Pick<T, K> asks: is a value with no properties at all still a valid instance? Only optional properties allow that, so the check is true exactly for the optional keys.
The as clause of a mapped type rewrites keys, and any key that evaluates to never is dropped from the result entirely. Our clause keeps optional keys and erases required ones:
[object Object]Walking { foo: number; bar?: string }:
// K = 'foo': probe is false → key becomes never → dropped
// K = 'bar': probe is true → key stays 'bar' → kept with value string
// result: { bar?: string }? comes fromThe solution never writes a ?, yet the result has one. A mapped type over keyof T (even with an as clause) is homomorphic: TypeScript copies each source property's readonly and ? modifiers onto the corresponding output property. bar was optional in T, so it stays optional in the result.
This matters for correctness, not just cosmetics: Equal<{ bar?: string }, { bar: string | undefined }> is false. A non-homomorphic detour, such as mapping over a precomputed union of optional keys, loses the modifier:
type Wrong<T> = { [K in OptionalKeys<T>]: T[K] }
// Wrong<{ bar?: string }> = { bar: string | undefined }, modifier lostThe ? is gone and the tests fail. Keeping K in keyof T as the iteration source and filtering via as is what preserves the modifiers.
{ foo: number; bar?: string } → { bar?: string }: the filter itself, with the ? intact.{ foo: undefined; bar?: undefined } → { bar?: undefined }: identical value types, so only a modifier-aware probe can tell the keys apart. The result still carries the ?.Together with GetRequired, RequiredKeys, and OptionalKeys, you now have the full toolkit for slicing object types by optionality, all built from one assignability trick and one filtering pattern.
This challenge is originally from here.