IsRequiredKey<T, K> reports whether every key in K is required on T. Optionality is a modifier, not a value type, so the check compares Pick against Required.
{ a: undefined } and { a?: undefined } are different types, and this challenge is about telling them apart.
IsRequiredKey<T, K> returns true if every key in K is a required property of T, and false otherwise. Because optionality is a modifier on the property rather than part of its value type, checking for undefined gets you nowhere: a property typed a: undefined is still required, while b?: string is optional. The solution plays Required and Pick against each other and lets structural assignability report the difference.
For example
type A = IsRequiredKey<{ a: number, b?: string },'a'> // true
type B = IsRequiredKey<{ a: number, b?: string },'b'> // false
type C = IsRequiredKey<{ a: number, b?: string },'b' | 'a'> // falseImplement a generic IsRequiredKey<T, K> that return whether K are required keys of T .
For example
type A = IsRequiredKey<{ a: number, b?: string },'a'> // true
type B = IsRequiredKey<{ a: number, b?: string },'b'> // false
type C = IsRequiredKey<{ a: number, b?: string },'b' | 'a'> // falseView on GitHub: https://tsch.js.org/2857
Change the following code to make the test cases pass (no type check errors).
/* _____________ Your Code Here _____________ */
type IsRequiredKey<T, K extends keyof T> = any
/* _____________ Test Cases _____________ */
import type { Equal, Expect } from '../helpers'
type cases = [
Expect<Equal<IsRequiredKey<{ a: number; b?: string }, 'a'>, true>>,
Expect<Equal<IsRequiredKey<{ a: undefined; b: string }, 'a'>, true>>,
Expect<Equal<IsRequiredKey<{ a: number; b?: string }, 'b'>, false>>,
Expect<Equal<IsRequiredKey<{ a: number; b?: string }, 'b' | 'a'>, false>>,
Expect<Equal<IsRequiredKey<{ a: undefined; b: undefined }, 'b' | 'a'>, true>>,
]
Unlock 170+ medium, hard, and extreme challenges to master advanced TypeScript.
Monthly subscription. Cancel anytime.
The entire solution is one comparison:
type IsRequiredKey<T, K extends keyof T> =
Pick<T, K> extends Required<Pick<T, K>> ? true : falseOne line, leaning on a subtle property of how TypeScript models optionality.
undefinedYour first instinct might be: "a key is optional if undefined is part of its type." But look at this test case:
type cases = [
Expect<Equal<IsRequiredKey<{ a: undefined; b: string }, 'a'>, true>>,
]Here a is explicitly typed as undefined, yet it's a required key: you must write { a: undefined, b: '...' }, and omitting a is an error. Optionality lives in the ? modifier on the property declaration, not in the value type. So the solution needs a way to interrogate the modifier itself.
Pick<T, K> builds a new object type containing only the properties named by K, keeping their modifiers intact. For T = { a: number; b?: string }:
// Pick<T, 'a'> = { a: number }
// Pick<T, 'b'> = { b?: string }
// Pick<T, 'a' | 'b'> = { a: number; b?: string }Note that Pick handles the union case for free: when K is 'a' | 'b', we get an object with both properties, so a single check can decide whether all of them are required. There's no distribution happening here (a conditional type only distributes when the checked type is a bare type parameter, and Pick<T, K> is not bare), which is exactly what we want: one optional key should make the whole answer false.
Required trickRequired<X> is the built-in utility that strips the ? modifier from every property (its implementation is { [P in keyof X]-?: X[P] }; the -? syntax removes the modifier). Now compare the picked type against its fully-required twin:
// Pick<T, 'a'> = { a: number } Required<...> = { a: number } → identical
// Pick<T, 'b'> = { b?: string } Required<...> = { b: string } → different!If none of the picked keys were optional, Required changes nothing and Pick<T, K> extends Required<Pick<T, K>> holds, so the answer is true. But if any key was optional, Required tightens it, and the check fails: { b?: string } is not assignable to { b: string }, because a value of the optional type might omit b entirely.
The direction of the check matters. Flipping it to Required<Pick<T, K>> extends Pick<T, K> would always be true, since a required property is always assignable to its optional counterpart. You must ask whether the possibly-optional side satisfies the required side.
IsRequiredKey<{ a: undefined; b: string }, 'a'> → true: Required<{ a: undefined }> is still { a: undefined }; on a property that was never optional, Required changes nothing, so the check passes.IsRequiredKey<{ a: undefined; b: undefined }, 'b' | 'a'> → true: both properties are required despite being typed undefined; picking both and requiring both changes nothing.IsRequiredKey<{ a: number; b?: string }, 'b' | 'a'> → false: one optional key in the union poisons the whole check, because the comparison happens on the combined object, not per key.When you need to reason about ? modifiers in the type system, don't inspect value types. Build two object types that differ only in the modifier and test assignability between them.
This challenge is originally from here.
Track your progress through 100+ hands-on challenges. Free, sign in with GitHub.
Or start solving right away: explore all TypeScript challenges