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:
Will be giving this a try.
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
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?
@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).
I have same issue. :(
Typescript Version : 3.4.5
@MufidJamaluddin duplicate of #7719
Workaround is use kelas!.iskelas
Same issue as @MufidJamaluddin
I have a checker in place to avoid duplicating code but Typescript ignores it and output this error.
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.
@marcj Same error, but as with @ktoto's, the issue has more to do with https://github.com/microsoft/TypeScript/issues/29042 and https://github.com/microsoft/TypeScript/issues/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');
}
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)
{
}
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;
}
}
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.
another example
class Test {
num?: number;
constructor(num?: number) {
this.num = num
}
checkIsPositive() {
return this.num && this.num > 0
}
doStuff() {
if (!this.checkIsPositive()) return;
console.log(this.num ** 4);
}
}
playground link: http://www.typescriptlang.org/play/?ssl=16&ssc=2&pln=1&pc=1#code/MYGwhgzhAEAqCmEAu0DeAoaXoDsCuAtgPwBcuhARvAE4Dc6m2wA9jstXsEs9QBT7EyAqtQCUaRtmxIAFgEsIAOgHQAvOQKToAXwZTgM+MADWASQgAFZhDlI5AN3i9xGKVOrwkeajmiyFyoTQAGTBfvJKKgB80AAMWrpaACbMAMpeAGYZzhJu2HIZ0LwAhP5KBkZmlta2Dk6i4h5ePvR5WCxszCDwiiDMAOa8ZYEE0ABUY9AALKKt2LqJ6EA
@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).
Another example below in case it helps.
http://www.typescriptlang.org/play/?noImplicitReturns=false#code/JYOwLgpgTgZghgYwgAgArAQawK4AcAickAzsgN4BQyyA2phAJ4BcyxYUoA5gLotm31myENgC2AI2i9khSMgC+yAD7JsIACYQYoCOoryKVZDDUIwwAPYhjodeix5ZKABS4MOAkQjEW9j0+IAGmQ4dXUobx9Wdi5g2wgADxYRCWgAShYnZVUNLR11ciNqYBhkZwBCNwdPEhpQ8MjuZRVK90cvYjqwiOJibhp4hO40wuox5AiwbCgQIoVDccnp6yr-Dq6G3v7B7iMDIA
why is this not allowed?
checkIsPositive(num: number | undefined): boolean {
return num > 0
}
why is this not allowed?
checkIsPositive(num: number | undefined): boolean { return num > 0 }
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.
@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?
@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?
@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.
@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)
}
Tracking at #7719
This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
@RyanCavanaugh did you link the wrong issue? That one has been closed since 2018.
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
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!
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.
@RyanCavanaugh did you link the wrong issue ? That one has been closed since 2018.
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.
Most helpful comment
@MufidJamaluddin duplicate of #7719
Workaround is use
kelas!.iskelas