0
class A {
    readonly is_prop: boolean;
    constructor(is_prop: boolean) {
        this.is_prop = is_prop;
    }
}

class B {
    readonly my_prop: boolean;
    constructor(my_prop: boolean) {
        this.my_prop = my_prop;
    }
}

type A1 = A & {is_prop: true};
type A2 = A & {is_prop: false};

type C = A1 | A2 | B;

const a: A = new A(true);
const b: A = new A(false);
const c: B = new B(true);

const e: A1 | null = a.is_prop ? a : null;

In the above example, why is the assignment for e giving error? Why is TS not inferring that is_prop will be true

Typescript playground

3
  • Intersection types in typescript behave as function overloading as far as I know. So saying A & {is_prop: true}; equals to a type that has both is_prop : boolean and is_prop: true. They aren't actually unified, which is what I believe you want which would be type A1 = Omit<A, 'is_prop'> & { is_prop:true}; Commented Apr 13, 2021 at 4:33
  • That still gives error on assignment Commented Apr 13, 2021 at 4:40
  • It's something along the lines of that. I am not entirely sure of the correct utility that's why I didn't put it as an answer. The point is as far as I know you can't achieve this by simple intersection. You will need to use utility types Commented Apr 13, 2021 at 4:42

3 Answers 3

1

The error is because a is of type A:

const a: A = new A(true);

In the ternary, you check that is_prop is true, but the variable a is still of type A. The property has been narrowed to true, but a hasn't changed types. As an example, this code would be valid based on your narrowing:

const trueVal: true | null = a.is_prop ? a.is_prop : null 

If you want to be able to narrow the type of a, you need to say that it can be one of several types by stating that it's a union:

const a: A1|A2 = new A(true);

playground with some more alternatives.

Sign up to request clarification or add additional context in comments.

1 Comment

This is the true explanation of the actual and the desired result.
0

You get that error because of this line of code

type A1 = A & {is_prop: true}; //for class a, is_prop is boolean

boolean is not a subset of true, so you can't assign class A to type A1:

you could try this:

type A1 = A & {is_prop: boolean};

or this:

const e: A1 | null = a.is_prop ? a as A1: null;

1 Comment

Please don't suggest unsafe castings when there are safe alternatives.
0

That's because that true doesn't equal boolean type

And I don't think you need to declare type of the variables a b c again

They can be directly implicitly declared through the following generics.

class A<T = boolean> {
  readonly is_prop: T extends boolean ? T : never;
  constructor(is_prop: T extends boolean ? T : never) {
    this.is_prop = is_prop;
  }
}

class B {
  readonly my_prop: boolean;
  constructor(my_prop: boolean) {
    this.my_prop = my_prop;
  }
}

type A1 = A & { is_prop: true };
type A2 = A & { is_prop: false };

type C = A1 | A2 | B;

const a = new A(true);
const b = new A(false);
const c = new B(true);

const e: A1 | null = a.is_prop ? a : null;

In this way, you don't have to declare type A1 A2 C and declare type A1 | null to e, and then you can get more simplified code like that

class A<T = boolean> {
  readonly is_prop: T extends boolean ? T : never;
  constructor(is_prop: T extends boolean ? T : never) {
    this.is_prop = is_prop;
  }
}

class B {
  readonly my_prop: boolean;
  constructor(my_prop: boolean) {
    this.my_prop = my_prop;
  }
}

const a = new A(true);
const b = new A(false);
const c = new B(true);

const e = a.is_prop ? a : null;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.