Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: Object is possibly 'undefined'

I'm curious as to why I would be getting a Typescript warning below.

There error appears on this line: a[k].toString().toLowerCase()

However I have the check if (a && a[k]) which should ensure that in the next line, a and a[k] cannot be undefined?

// Return coins that match text search by currency symbol or name.
export const findAsset = (txt: string, assets: IAsset[] | null[]) => {
  // assets will exist here...
  if (assets) {
    // Typescript error here...
    const checkText = (k: string, a: IAsset | null) => {
      if (a && a[k]) {
        return (textMatch(txt.toLowerCase(), a[k].toString().toLowerCase()) ? a : null);
      }
    }
    const curriedCheckText = R.curry(checkText);
    const byName = R.map(curriedCheckText('name'), assets);
    const bySymbol = R.map(curriedCheckText('currency'), assets);
    const matchNames = R.reject(R.isNil, byName);
    const matchSymbols = R.reject(R.isNil, bySymbol);
    const combinedSearch = (matchNames.concat(matchSymbols));
    return R.uniq(combinedSearch);
  }
  else {
    return [];
  }
};

This is the shape of the IAsset interface

export interface IAsset {
  [key: string]: string | number | undefined | boolean;
  availableSupply?: string;
  currency: string;
  exchange: string;
  exchange_base?: string;
  marketCap: number;
  name: string;
  percentage?: number;
  price: number;
  position?: number;
  value?: number;
  inWatchlist?: boolean;
}

enter image description here

tsconfig

{
  "compilerOptions": {
    /* Basic Options */
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */,
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
    "lib": [
      "dom",
      "es2015",
      "es2016",
      "es2017",
    ], /* Specify library files to be included in the compilation. */,
    "allowJs": true,                       /* Allow javascript files to be compiled. */
    "checkJs": true,                       /* Report errors in .js files. */
    "jsx": "react",                     /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
    "declaration": true,                   /* Generates corresponding '.d.ts' file. */
    "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
    "sourceMap": true,                     /* Generates corresponding '.map' file. */
    // "outFile": "./",                       /* Concatenate and emit output to single file. */
    // "outDir": "./",                        /* Redirect output structure to the directory. */
    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                     /* Enable project compilation */
    // "removeComments": true,                /* Do not emit comments to output. */
    "noEmit": true,                        /* Do not emit outputs. */
    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
    "resolveJsonModule": true,
    "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */

    /* Strict Type-Checking Options */
    "strict": true /* Enable all strict type-checking options. */,
    "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
    "strictNullChecks": true /* Enable strict null checks. */,
    "strictFunctionTypes": true /* Enable strict checking of function types. */,
    "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */,
    "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
    "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
    "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,

    /* Additional Checks */
    // "noUnusedLocals": true,                /* Report errors on unused locals. */
    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */

    /* Module Resolution Options */
    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "typeRoots": [],                       /* List of folders to include type definitions from. */
    // "types": [],                           /* Type declaration files to be included in compilation. */
    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */

    /* Source Map Options */
    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */

    /* Experimental Options */
    "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
  },
  "include": [
    "components/**/*",
    "pages/**/*",
  ],
  "exclude": [
    "node_modules",
    "styles",
    "_document.tsx"
  ]
}
like image 683
Leon Gaban Avatar asked Mar 08 '19 17:03

Leon Gaban


People also ask

How do I fix Object is possibly undefined in TypeScript?

The "Object is possibly 'undefined'" error occurs when we try to access a property on an object that may have a value of undefined . To solve the error, use the optional chaining operator or a type guard to make sure the reference is not undefined before accessing properties.

How do you prevent undefined in TypeScript?

To avoid undefined values when using or accessing the optional object properties, the basic idea is to check the property value using an if conditional statement or the optional chaining operator before accessing the object property in TypeScript.

How do you ignore an Object is undefined?

Another common way to avoid getting the "Object is possibly null or undefined " error is to use the logical AND (&&) operator. Copied!

How do I remove undefined in TypeScript?

Use the NonNullable utility type to remove null and undefined from a type in TypeScript. The NonNullable utility type constructs a new type with null and undefined excluded from the type.

What does'object is possibly'undefined'mean in typescript?

"Object is possibly 'undefined'." in TypeScript - Stack Overflow The code below throws Object is possibly 'undefined'. at obj.field1 += ',' + v;. TypeScript says the obj may be undefined, but the obj can not be undefined at this point because {field1: 'testtest'} is assigned in case map.get (key) returns undefined.

How to throw object is possibly undefined in typescript?

The code below throws Object is possibly 'undefined'. at obj.field1 += ',' + v;. TypeScript says the obj may be undefined, but the obj can not be undefined at this point because {field1: 'testtest'} is assigned in case map.get (key) returns undefined.

Why does JavaScript return undefined when I ask for a property?

When you have a JavaScript object and you ask for a property that doesn’t exist, JavaScript will return undefined rather than throwing an error. In strict mode, this means a couple of things. First, if you don’t tell TypeScript that a property is optional, it will expect it to be set.

What is the difference between null and undefined in JavaScript?

Because undefined is identical to null in a loose comparison, it covers both undefined and null. If you are certain that the property cannot have a null value, you can use the non-null assertion operator. name?: { In TypeScript, the exclamation mark (!) is the non-null assertion operator.


4 Answers

There are two ways to that I can think of to get rid of the error.

The first way I can think of is to use a fallback with the || operator, which would turn

a[k].toString().toLowerCase() 

Into this, so if the value is falsy, then use an empty string.

(a[k] || '').toString().toLowerCase() // Or with optional chaining a[k]?.toString().toLowerCase() || '' 

The other way is to save the value to a variable and check the new variable. Which then makes this

if (a && a[k]) {   return textMatch(txt.toLowerCase(), a[k].toString().toLowerCase()) ? a : null; } 

Become this:

let v = a ? a[k] : null if (v) {   return textMatch(txt.toLowerCase(), v.toString().toLowerCase()) ? a : null; } 
like image 97
Get Off My Lawn Avatar answered Sep 26 '22 13:09

Get Off My Lawn


Typescript doesn't keep type information about values at specific array indices. For example, this is an error:

function test(a: (number | string)[]) {     if (typeof a[3] === "number") {         const num: number = a[3];     } } 

To get something that can have type information, you could put a[k] into some other variable like this:

const checkText = (k: string, a: IAsset | null) => { if (a) {   const atK = a[k];   if (atK) {     return (textMatch(txt.toLowerCase(), atK.toString().toLowerCase()) ? a : null);   } } 

or you could cast it, because you know more about the types than the type system:

const checkText = (k: string, a: IAsset | null) => {   if (a && a[k]) {     return (textMatch(txt.toLowerCase(), (a[k] as string | number | true).toString().toLowerCase()) ? a : null);   } } 

or you could avoid having to worry about undefineds by using string interpolation:

const checkText = (k: string, a: IAsset | null) => {   if (a && a[k]) {     return (textMatch(txt.toLowerCase(), `${a[k]}`.toLowerCase()) ? a : null);   } } 

Ideally the type system would take care of these things, but I'm sure there's some reason why this kind of type information isn't available in general.

like image 37
Half Avatar answered Sep 22 '22 13:09

Half


Not sure if this is 100% applicable to your question, ever since TypeScript 3.7 is released, the use of optional chaining (? operator) is definitely another relevant solution to be explored. You may install the latest stable version of TypeScript:

npm install typescript

As such, checkText can be simplified by using the optional chaining operator.

const checkText = (k: string, a: IAsset | null) => {
  return (textMatch(txt.toLowerCase(), a?.[k].toString().toLowerCase()) ? a : null);
}
like image 39
wentjun Avatar answered Sep 25 '22 13:09

wentjun


Supress the error in VUE templates

If you understood the error and want to know how to handle it in VUE templates: simply put a condition in the parent element

<div v-if="user">
   <p>Hallo {{user.firstName}}</p>
</div>
like image 20
agoldev Avatar answered Sep 23 '22 13:09

agoldev