Required<T> does not work for secondary lookup types

Required<T> does not work for secondary lookup types

If I have some code like:

type Bravo = {
  b?: string; 
}

type Charlie = {
  c:Bravo['b']
}

function acceptsCharlie(value: Charlie){
  //'value.c' is possibly 'undefined'.(18048)
  value.c.split('');
}

This makes sense - the type Charlie inherits the optional string from Bravo.

We can solve this by using the Required utility type:

type Bravo = {
  b?: string; 
}

type Charlie = {
  //  👇
  c:Required<Bravo>['b']
}

function acceptsCharlie(value: Charlie){
  // no error here 
  value.c.split('');
}

Easy.

However, if I add one more layer of typing look-up-ing:

type Alpha = {
  a?: string; 
}

type Bravo = {
  b: Alpha['a']; 
}

type Charlie = {
  c: Required<Bravo>['b']
}

function acceptsCharlie(value: Charlie){
  //'value.c' is possibly 'undefined'.(18048)
  value.c.split('');
}

The Required no longer solves the problem for me.

What gives?

Answer

The difference between ? and | undefined is that in the first situation the field can be skipped, while in the second field needs to exist, even if it can have undefined as a value.

Required works on only optional fields. If a field is typed as field : X | undefined, then Required will have no effect on it.

enter image description here

Read more

Whereas when it is typed as field? : X, then Required works:

enter image description here

Playground

What you are doing with the indirection is that now Bravo definitely has the field b, it might be undefined or string, but it is not optional. So in that case Required can not do much.

Excerpt from the release notes of TS 2.8:

Using this ability, lib.d.ts now has a new Required type. This type strips ? modifiers from all properties of T, thus making all properties required.

Enjoyed this article?

Check out more content on our blog or follow us on social media.

Browse more articles