简体   繁体   中英

Strange behaviour of callback function in Typescript

I'm trying to solve problem with filter array function in Typescript, code below:

type Tag = string

type Args = {
  tags?: Tag[]
}

const func = async (args: Args) => {
  if (args.tags) {
    const entities = [
      { tagId: '1' },
      { tagId: '2' }
    ];
    
    const filtered = entities.filter(entity => args.tags.includes(entity.tagId));
    const mapped = args.tags.map(tag => tag + '_test');  
  }
};

This code throws TS2532 error: Object is possibly undefined when I try to filter the entities array. For some reasons TS interpreter thinks that args.tags can be undefined, so include function can't be called. But as you see, there is a check above, also the map function works fine.

What can be wrong here? Any thoughts are welcome.

Thanks.

TypeScript doesn't know that filter 's callback runs immediately, so if for example filter 's callback ran later (let's say on a setTimeout ), your code would crash under a call like this:

const a: Args = { tags: [] };
await func(a);
a.tags = undefined;
// Later, the callback runs and accesses 'includes' of the undefined we just set

You can either use !

entity => args.tags!.includes ...

Or stash the value in a const

const tags = args.tags;
if (tags) {
  // ...
  const filtered = entities.filter(entity => tags.includes ...
type Args = {
  tags?: Tag[] // this means --> tags: Tag[] | undefined
}

Use optional chaining and nullish coalescing inside Array.filter() .

const filtered = entities.filter(entity => args?.tags?.includes(entity.tagId));

The entities array is an array of objects, but the TypeScript compiler is inferring the type as any[] and doesn't know that the object will contain the tagId property. Your code should run with the following change:

    const entities: {tagId:string}[] = [

Additionally, you want to account for the potentially undefined tags property in the Args type:

    const filtered = entities.filter(entity => args.tags && args.tags.includes(entity.tagId));

Full code:

type Tag = string;

type Args = {
  tags?: Tag[];
};

const func = async (args: Args) => {
  if (args.tags) {
    const entities: { tagId: string; }[] = [
      { tagId: '1' },
      { tagId: '2' }
    ];

    const filtered = entities.filter(entity => args.tags && args.tags.includes(entity.tagId));
    const mapped = args.tags.map(tag => tag + '_test');
  }
};

I tested this code with ts-node and tsc (with --lib es2016.array.include ) and received no errors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM