rpadovani
rpadovani

Reputation: 7360

Typescript isn't able to understand a property is not null for sure

interface A {
    id?: string
}

interface B {
    id: string
}

function test(a: A, b: A) {
    if (!a.id && !b.id) { return }

    let c: B = {
        id: a.id || b.id
    }
}

Link to playground

The error I get:

Types of property 'objectId' are incompatible. Type 'string | undefined' is not assignable to type 'string'. Type 'undefined' is not assignable to type 'string'.

How can I go around this? One between a.id an b.id is defined, so this shouldn't be a problem.

Of course disabling strickNullChecks isn't an option.

tsc 2.4.2

Upvotes: 1

Views: 305

Answers (2)

jcalz
jcalz

Reputation: 329953

As @daniel-klischies says, TypeScript's control-flow analysis is not up to the task and you need to help it. In this case, TypeScript is not trying to apply De Morgan's Laws to see that (!a.id && !b.id) is the same as !(a.id || b.id). It would probably be possible to implement, but spending compiler time checking for this is probably not worth the added bit of control-flow completeness.

But since we know that those are identical, we can help TypeScript by using that identity:

function test(a: A, b: A) {
  const id = a.id || b.id;
  if (!id) return;
  let c: B = { id: id };
}

This type checks as desired. Good luck!

Upvotes: 1

Daniel Klischies
Daniel Klischies

Reputation: 1135

Typescript cannot automatically deduce that at the line where you set your c at least one id must be non-null. The reason for this is based on computability theory (i.e. it is just not possible to do this reliably and for all edge-cases).

So you will have to add a typecast manually:

let c: B = {
    id: <string>(a.id || b.id)
}

Upvotes: 1

Related Questions