Skip to content
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

Object is possibly 'undefined' #29642

Closed
troy351 opened this issue Jan 30, 2019 · 29 comments
Closed

Object is possibly 'undefined' #29642

troy351 opened this issue Jan 30, 2019 · 29 comments
Labels
Duplicate An existing issue was already created

Comments

@troy351
Copy link

troy351 commented Jan 30, 2019

TypeScript Version: 3.4.0-dev.201xxxxx

Search Terms: Object undefined

Code turn strictNullChecks on

// A *self-contained* demonstration of the problem follows...
// Test this by running `tsc` on the command-line, rather than through another build tool such as Gulp, Webpack, etc.
type EventType = 'click' | 'dblclick'

const handlerMap: { [P in EventType]?: any[] } = {}

function addHandler<P extends EventType>(evType: P) {
  const handlerList = handlerMap[evType] || []
  handlerList.push({}) // Error here: Object is possibly 'undefined'
  handlerMap[evType] = handlerList
}

Expected behavior: No Error

Actual behavior: Shows Error

Playground Link: http://www.typescriptlang.org/play/#src=type%20EventType%20%3D%20'click'%20%7C%20'dblclick'%0Aconst%20handlerMap%3A%20%7B%20%5BP%20in%20EventType%5D%3F%3A%20any%5B%5D%20%7D%20%3D%20%7B%7D%0Afunction%20addHandler%3CP%20extends%20EventType%3E(evType%3A%20P)%20%7B%0A%20%20const%20handlerList%20%3D%20handlerMap%5BevType%5D%20%7C%7C%20%5B%5D%0A%20%20handlerList.push(%7B%7D)%20%2F%2F%20Error%20here%3A%20Object%20is%20possibly%20'undefined'%0A%7D%0A

Related Issues:

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Jan 30, 2019
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.4.0 milestone Jan 30, 2019
@collin5
Copy link
Contributor

collin5 commented Feb 1, 2019

Will be giving this a try.

@ktoto
Copy link

ktoto commented Feb 18, 2019

It's the same issue?

interface State {
    [i: string]: {data?: {id:string}} | undefined
 }

declare var x: State;

const id = 'xx';
const y = x[id] && x[id].data; // Error: Object is possibly 'undefined' (with strictNullChecks
const yId = y && y.id; // correct type – string | undefined

Playground link : https://www.typescriptlang.org/play/#src=interface%20State%20%7B%0D%0A%20%20%20%20%5Bi%3A%20string%5D%3A%20%7Bdata%3F%3A%20%7Bid%3Astring%7D%7D%20%7C%20undefined%0D%0A%20%7D%0D%0A%0D%0Adeclare%20var%20x%3A%20State%3B%0D%0A%0D%0Aconst%20id%20%3D%20'xx'%3B%0D%0A%0D%0Aconst%20y%20%3D%20x%5Bid%5D%20%26%26%20x%5Bid%5D.data%3B%0D%0A%0D%0Aconst%20yId%20%3D%20y%20%26%26%20y.id%3B%0D%0A

@weswigham
Copy link
Member

#29317 should be the fix here (which I have mentioned in @collin5 's PR). Specifically, the type of a || b should be calculated as (typeof a & not FalseyTypes) | typeof b, where FalseyTypes is 0 | false | "" | undefined | null.

@RyanCavanaugh given that, should we move this out of 3.4, since we won't be shipping them in 3.4?

@weswigham
Copy link
Member

@ktoto your issue is different - since State has an index signature, we don't track refinements on individual property lookups within it - see #17960 and #29042 (and go updoot those issues so they don't sit in the backlog).

@MufidJamaluddin
Copy link

I have same issue. :(

Typescript Version : 3.4.5

undefined

@troy351
Copy link
Author

troy351 commented May 10, 2019

@MufidJamaluddin duplicate of #7719

Workaround is use kelas!.iskelas

@micky2be
Copy link

micky2be commented Jun 13, 2019

Same issue as @MufidJamaluddin
I have a checker in place to avoid duplicating code but Typescript ignores it and output this error.

@weswigham weswigham added the Fix Available A PR has been opened for this issue label Jul 15, 2019
@marcj
Copy link

marcj commented Aug 6, 2019

Screenshot 2019-08-06 at 15 45 00

I got the exact same issue since upgrading to newer version 3.5.2. I honestly don't know what to do as this worked for ages. Forcing with ! is no option for me.

@MBerka
Copy link

MBerka commented Aug 7, 2019

@marcj Same error, but as with @ktoto's, the issue has more to do with #29042 and #17960. Ultimately, the question is how much TypeScript can be asked to simulate when checking keys. 3.5.3 seems to handle checking of a constant string index whose value was set with a constant string index after initialization, and of a constant variable index whose value was defined on declaration, but not a variable index for a value assigned after initialization.

const myObj: { prop?: number } = {};
myObj['prop'] = 1;
if (myObj['prop']) {
    myObj['prop'] = 1 * myObj['prop']; // No error
}
const field = 'prop'
if (myObj[field]) {
    myObj[field] = 1 * myObj[field]; // Possibly undefined
}
const otherObj = {prop: 1};
if (otherObj[field]) {
    otherObj[field] = 1 * otherObj[field]; // No error
}

Your case stands out because it requires dynamic assignment, dynamic retrieval, and continued access to the original object. Casting workaround that could get tedious:

interface KeyType {setValue: (input: string) => void};
const myObj: { aa?: KeyType} = {};
const key = 'aa';
myObj[key] = {setValue: (input): void => {console.log(input);}}

if (myObj[key]) {
    (myObj[key] as KeyType).setValue('bb');
}

@npenin
Copy link

npenin commented Aug 29, 2019

same issue for me with this code: https://www.typescriptlang.org/play/index.html?target=6#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDgYQgFsjMkATAKAG9K565sIl0EBzALgOdbYFcomGAmYBnANx0GyAFbBsMAPxdRMKMjYBtALqSAvpUqhIsRCjRZccABIwYYQi3YChIpHFCoKo7k-6DhZhopeiJgGAALCHIVNQ1JBjgoCD5UWPUkNn1DY2h4ZFQMHDxHXhdApFFgxM0Aa2AAT3SNbS4+CmBWJGByOAAfXzKAtwSGCLswZXpbe1LnYeZ9SSNwPLNCyxKeedcg2kTZeSVmzJ1swwA3TCg4TA5CEjJyAF5qRm3Oaj09SSubsGSYDQMAa1ggABtyGhXgCIEDYA0AHKYMIcADkqFUaJ+hgQ6AAFJg4AAyYm3AB0TD8mlh8JBYMhaHJtOBSJRwG0JLJmEpHxpgNZDKhUGZAoRyLC2nJhwUAEpgnogA

export interface Command
{
    config: Configurations;
    inject?: string[];
}

export interface HttpConfiguration extends Configuration
{
    method: string;
    route: string;
}

export interface Configurations
{
    [key: string]: undefined | Configuration;
    http?:  HttpConfiguration;
};

export interface Configuration
{
    inject?: string[];
}


var a:Command={ config:{}};
var propertyHolder={propertyName:'test'};

if(a && a.config[propertyHolder.propertyName] && a.config[propertyHolder.propertyName].inject)
{
}

@chamie
Copy link

chamie commented Sep 24, 2019

Another example without dynamic property access:

import React from "react";

interface IPropsPrivate<T> {
  someField: T;
  maybeRows?: string[];
}

type IProps<T> = T extends string ? never : IPropsPrivate<T>;

class Class1<T> extends React.PureComponent<IProps<T>> {
  public render() {
    const maybeRows = this.props.maybeRows;
    const notUndefined = maybeRows === undefined ? [] : maybeRows;

    notUndefined.filter(() => true); // Error: Object is possibly 'undefined'.

    const twiceGuaranteedUndefined = notUndefined || [];

    twiceGuaranteedUndefined.filter(() => true); // Error: Object is possibly 'undefined'.

    return null;
  }
}

// This works fine though
class Class2<T> extends React.PureComponent<IProps<T>> {
  public render() {
    const { maybeRows = [] } = this.props;

    maybeRows.filter(() => true);

    return null;
  }
}

>>Playground

@joelalejandro
Copy link

Another example, tried on 3.7.0-dev.20190928:

type DialogProps = {
  buttons?: {
    primary: boolean;
    secondary?: boolean;
  };
};

function Dialog({ buttons }: DialogProps) {
    const atLeastOneButton = buttons !== undefined;

    if (atLeastOneButton) {
      const label = buttons.primary; 
      // throws "Object is possibly 'undefined' over `buttons`
    }
}

Apparently, the compiler doesn't understand that atLeastOneButton is performing a not-undefined check for the same buttons variable.

Playground link
https://www.typescriptlang.org/play/?ts=Nightly#code/C4TwDgpgBAIglgQwDYHsDmAFATisBnKAXigG8AoKKAIwFdhgUA7PAfgC5SLKows4BbBFhAcqKFEggJGAbi6U8EAMZMAJkJDtq4ydLmUAvnKNkyAMxqMlwOE1iJUaABQlqdBsygGO8ZOmy4eACUnNxQKszAUAjAADJSeMAA8owQAELudsS09EwEAISExJaqEGZwqapQAGTVbrnMAHS8AhpQhcWMpeWVcvJQcGZQTjHxCIkp6ZmMIeRhlBGJUEgIVBBIRPUeeM18gsL63AZkBkA

@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 3.7.1 milestone Oct 29, 2019
paroxp added a commit to alphagov/paas-admin that referenced this issue Dec 6, 2019
There is a problem with a new version of TypeScript that hasn't been
occuring before. It's related to Diagnostics being enabled and reporting
on some [practically non-existing issues][1], such as:

> Object is possibly 'undefined'

This could be avoided by adding the `!` symbol to the object which
tricks the `tsc` to trust the developer.

I see that as a scary practice, which could quickly turn into something
like `font-size: 10px !important;`...

Instead, we're telling `jest`, not to run these diagnostics itself, as
personally I trust that linter and `tsc` itself will be able to handle
it and these don't throw these errors.

Saying that, it makes me think there is some other configuration to
jest, that could allow us keep the diagnostics, as well as act the same
way the linter/tsc do.

Disabling it for now, seems like sane approach.

[1]: microsoft/TypeScript#29642
@weswigham
Copy link
Member

@rasmus-storjohann-PG gunna need some more information on the shapes of the types in question to get an actual repro of that - I'd also open a new issue, since it looks like a pretty distinct bug (just resulting in the same diagnostic message).

@gkamperis
Copy link

why is this not allowed?

checkIsPositive(num: number | undefined): boolean {
        return num > 0
    }    

playground

@MBerka
Copy link

MBerka commented Jan 12, 2020

why is this not allowed?

checkIsPositive(num: number | undefined): boolean {
        return num > 0
    }    

playground

Because it does not make sense (in a strongly typed language) to supply a non-numerical value to an operation meant to compare two numbers. JavaScript's answer (always false if either argument cannot be converted into a number) is arbitrary. This is correct behavior unrelated to this issue.

@gkamperis
Copy link

gkamperis commented Jan 12, 2020

@MBerka I disagree. I specifically typed the argument. I know it can be undefined. I am not accessing any object properties/methods. The comparison is valid JS why am I prevented from running?

@troy351
Copy link
Author

troy351 commented Jan 13, 2020

@gkamperis That's valid JS but bad part of JS which TS whats to get rid of.
Since undefined > 0 === false doesn't mean undefined is smaller than 0, right?

@gkamperis
Copy link

@troy351 num ? (num > 0) : false does not mean num is smaller than 0 either. I do not see the relevance.

This check is about NPEs only. There is no chance an NPE can happen here. So the message is not valid.

@troy351
Copy link
Author

troy351 commented Jan 13, 2020

@gkamperis Yeah, for the complier, num !== undefined ? (num > 0) : false and num > 0 comes out the same result. But for progammers, it's obvious the first one is better.

TS was made for JS users to make less mistakes and that's how TS did it.

Consider this one

checkIsPositive(num: number | undefined): boolean {
        return !(num <= 0)
}   

@RyanCavanaugh RyanCavanaugh removed Bug A bug in TypeScript Fix Available A PR has been opened for this issue labels Jan 23, 2020
@RyanCavanaugh RyanCavanaugh removed this from the Backlog milestone Jan 23, 2020
@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jan 23, 2020
@RyanCavanaugh
Copy link
Member

Tracking at #7719

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@andreialecu
Copy link

@RyanCavanaugh did you link the wrong issue? That one has been closed since 2018.

@ecklf
Copy link

ecklf commented May 15, 2020

Also having this issue

Code

type Key = "key1" | "key2";
type Keys = {
  [P in Key]?: { keyProp: string };
};

const keys: Keys = {
  key1: { keyProp: "keyPropVal" },
};

let someKey!: Key;

if (keys[someKey]) {
  // This shows Object is possibly 'undefined'
  keys[someKey].keyProp;
}
Output
"use strict";
const keys = {
    key1: { keyProp: "keyPropVal" },
};
let someKey;
if (keys[someKey]) {
    // This shows Object is possibly 'undefined'
    keys[someKey].keyProp;
}
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

@Casa-Cloud
Copy link

Casa-Cloud commented May 16, 2020

Hello All,

The object is possibly 'undefined',

This warning is shown by the editor when we provide the body definition of some variable in the form of the interface and add some of the fields as optional

Eg:-

interface Blog {
  autherName: string;
  contents?: any[],
  id: string
}

While using this interface Blog in our code if we are trying to do some loop on Blog.contents
Eg:-

const newBlog: Blog = JSON.parse(blog);
for (const content of newBlog.contents) {`
   console.log("content ", content );
}

This will error in for loop declaration for (const content of newBlog.contents) because we have said in an interface that contents property in Blob object can be or cannot be there hence we first need to check in code if the property is present then we should use it. As follows

Eg:-

if (newBlog.contents && newBlog.contents.length > 0) {
    for (const content of newBlog.contents) {
      console.log("content ", content );
   }
}

This is one way to resolve this warning.

Happy Coding!

@harishkshetty
Copy link

Hello All,

The object is possibly 'undefined',

This warning is shown by the editor when we provide the body definition of some variable in the form of the interface and add some of the fields as optional

Eg:-

interface Blog {
  autherName: string;
  contents?: any[],
  id: string
}

While using this interface Blog in our code if we are trying to do some loop on Blog.contents
Eg:-

const newBlog: Blog = JSON.parse(blog);
for (const content of newBlog.contents) {`
   console.log("content ", content );
}

This will error in for loop declaration for (const content of newBlog.contents) because we have said in an interface that contents property in Blob object can be or cannot be there hence we first need to check in code if the property is present then we should use it. As follows

Eg:-

if (newBlog.contents && newBlog.contents.length > 0) {
    for (const content of newBlog.contents) {
      console.log("content ", content );
   }
}

This is one way to resolve this warning.

Happy Coding!

Thanks alokadhao20,It solved issue for me.

@subins2000
Copy link

@RyanCavanaugh did you link the wrong issue ? That one has been closed since 2018.

@microsoft microsoft locked as off-topic and limited conversation to collaborators Jul 8, 2020
@RyanCavanaugh
Copy link
Member

Locking because everyone is posting about all of their "is possibly undefined" code snippets here without context for the original post.

The inability to handle x || [] when x is a type parameter is a current design limitation wherein we need to have some new mechanism for describing higher-order facts about generic type parameters; today if something is T and we know it's truthy or falsy, there's no mechanism for us to better describe its assignability to any given target type. Everything we've tried so far here has been a performance or usability disaster so it's on the back burner for the time being until we can hopefully have some better insight on how to represent this in a more ergonomic/efficient way.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

Successfully merging a pull request may close this issue.