Dakito
Dakito

Reputation: 387

Intellisense says the property does not exist in Class1 and Class2

Here is my code, i know that is a bit long but not hard to understand. It showing error in the last line and also losing intellisense there. "Property 'test' does not exist on type 'Test | Test2'." As you can see clearly the 'test' property is obviously within the 'Test' class. So what's wrong?

class Test{
    test: string = 'test';
}
class Test2{
    test2: string = 'test2';
}

function isTest(obj: Test|Test2): obj is Test{
    if(obj instanceof Test)
    return true;
    else
    return false;
}

function test(obj: Test|Test2): Test|Test2{
    if(isTest(obj))
    return obj as Test;
    else
    return obj as Test2;
}



let varTest: Test = new Test();
console.log(test(varTest).test); //this line is broken

Upvotes: 2

Views: 81

Answers (2)

Christian Santos
Christian Santos

Reputation: 5456

Why it's not working

The return type of test() is static in the sense that it's return value will always be Test | Test2, regardless of how it's called. This means that varTest can be Test OR Test2 (which does not contain a test property), so the error is correctly letting you know that the property you are trying to access may not exist.

If we need a dynamic return type, we need to turn to generics, overloads, or conditional types, depending on the problem. In your case, we can use function overloads.

Working solution

Function overloads allow you to specify return types that are conditional on the input types. To make your test() function work as expected, we overload it with two conditions: one for Test and another for Test2:

function test(obj: Test): Test; // given Test, return Test
function test(obj: Test2): Test2; // given Test2, return Test2
function test(obj: Test | Test2): Test | Test2{
    if(isTest(obj))
        return obj as Test;
    else
        return obj as Test2;
}

let varTest: Test = new Test();
console.log(test(varTest).test); // OK

let varTest2: Test2 = new Test2();
console.log(test(varTest2).test); // Error! 'test' does not exist in Test2

See playground link.

Upvotes: 1

inoabrian
inoabrian

Reputation: 3800

@Dakito, this typescript Union type is quite tricky.

But if you take a look here: Typescript Union Types you can see you will have to add a check to make sure you are safely accessing a field or method from an object without assuming it will not fail.

// this line is broken
let varTest: Test = new Test();

let testResult: Test | Test2 = test(varTest);

// Adding these checks here will help you safely access the correct values.
// You can also improve this code by using User-Defined Type Guards
if(<Test>testResult.test) {
    console.log((<Test>testResult).test);
} else if(<Test2>testResult.test2) {
    console.log((<Test2>testResult).test2);
}

Upvotes: 0

Related Questions