Skip to content

Wrong Type Error on missing property when using optional chaining #35543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Ninerian opened this issue Dec 6, 2019 · 4 comments
Closed

Wrong Type Error on missing property when using optional chaining #35543

Ninerian opened this issue Dec 6, 2019 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Ninerian
Copy link

Ninerian commented Dec 6, 2019

TypeScript Version: 3.7.2

Search Terms:

  • optional chainging
  • missing property

Expected behavior:
Typescript should handle the optional method as same as optional properties

Actual behavior:
Typescript throws a warning:

Property 'foo' does not exist on type 'A | B'.
  Property 'foo' does not exist on type 'B'.(2339)

The code itself is doing what I expected. When the method is not found, it uses the right hand value.

Related Issues:
No issue found

Code

class A {
    public foo() {
        return 'foo'
    }
    public bar() {
        return 'bar'
    }
}

class B {
    public bar() {
        return 'bar'
    }
}

const x = (F: typeof A | typeof B) => {
    const f = new F();

    return f.foo?.() ?? 'baz';
}

console.log(x(A)); // foo
console.log(x(B)); // baz
Output
"use strict";
class A {
    foo() {
        return 'foo';
    }
    bar() {
        return 'bar';
    }
}
class B {
    bar() {
        return 'bar';
    }
}
const x = (F) => {
    var _a, _b, _c;
    const f = new F();
    return _c = (_b = (_a = f).foo) === null || _b === void 0 ? void 0 : _b.call(_a), (_c !== null && _c !== void 0 ? _c : 'baz');
};
console.log(x(A)); // foo
console.log(x(B)); // baz
Compiler Options
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "useDefineForClassFields": false,
    "alwaysStrict": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "downlevelIteration": false,
    "noEmitHelpers": false,
    "noLib": false,
    "noStrictGenericChecks": false,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "esModuleInterop": true,
    "preserveConstEnums": false,
    "removeComments": false,
    "skipLibCheck": false,
    "checkJs": false,
    "allowJs": false,
    "declaration": true,
    "experimentalDecorators": false,
    "emitDecoratorMetadata": false,
    "target": "ES2017",
    "module": "ESNext"
  }
}

Playground Link: Provided

@dragomirtitian
Copy link
Contributor

dragomirtitian commented Dec 6, 2019

This is not related to optional chaining. Unions have never allowed access to non-common members of union constituent:

const x = (o: { a: number } | { b: string }) => {
    o.a // error 
    if (o.a) { // error even though could conceivable be a type guard
        
    }
    if ('a' in o) { // ok type guard
       o.a // o is now narrowed to  { a: number }
    }
}

Playground Link

So conceivable we can write this more verbose version of your code:

const x = (F: typeof A | typeof B) => {
    const f = new F();

    return ('foo' in f ? f.foo() : null) ?? 'baz';
}

Playground Link

@dragomirtitian
Copy link
Contributor

Related to #35542

@Ninerian
Copy link
Author

Ninerian commented Dec 6, 2019

@dragomirtitian thanks for the explanation and the example. Can be closed from my side.

@jcalz
Copy link
Contributor

jcalz commented Dec 6, 2019

Duplicate of (the declined 😞) #33736

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Dec 9, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants