0

I'm trying to cast an object (the Item interface) to it's derived interface (FileItem or FolderItem)

interface Item {
id: number;
name: string;
}

interface FileItem extends Item {
 size: number;
}

interface FolderItem extends Item {
  parent_id: number;
}

const files: FileItem[] = [ {id: 1, name: 'file name 1', size: 1024 } ];
const folders: FolderItem[] = [ {id:1, name: 'folder 1', parent_id: 0} ];
const items = new Array<Item>(...files, ...folders);
items.forEach(i => {
  const isFile = i as FileItem;
  if (isFile) {
   writeLog('its a file');
 }
 else writeLog('its a folder');
})

in the foreach loop, typescript cast all items to a FileItem which is wrong, because it should cast to FileItem and FolderItem, take a look at the demo

is this the way typecasting in typescript works? how to cast to the proper interface.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Arh Hokagi
  • 170
  • 5
  • 21

1 Answers1

0

This isn't how typecasting in Typescript works. During compilation, all types are stripped so your code will end up something like this:

const files = [ {id: 1, name: 'file name 1', size: 1024 } ];
const folders = [ {id:1, name: 'folder 1', parent_id: 0} ];
const items = new Array(...files, ...folders);
items.forEach(i => {
  const isFile = i;
  if (isFile) {
   writeLog('its a file');
 }
 else writeLog('its a folder');
})

isFile will always be truthy as i is truthy.

To get what you're after, you'll need to use a type guard. For example:

function isFile(item: Item): item is FileItem {
  return (item as FileItem).size !== undefined;
}

This checks if an Item has the property size. If it does, it must be a FileItem. You would use it like this:

items.forEach(i => {
  if (isFile(i)) {
    writeLog('its a file');
  }
  else writeLog('its a folder');
})

Full code:

interface Item {
  id: number;
  name: string;
}

interface FileItem extends Item {
  size: number;
}

interface FolderItem extends Item {
  parent_id: number;
}

function isFile(item: Item): item is FileItem {
  return (item as FileItem).size !== undefined;
}

const files: FileItem[] = [ {id: 1, name: 'file name 1', size: 1024 } ];
const folders: FolderItem[] = [ {id:1, name: 'folder 1', parent_id: 0} ];
const items = new Array<Item>(...files, ...folders);
items.forEach(i => {
  if (isFile(i)) {
   writeLog('its a file');
 }
 else writeLog('its a folder');
})

Also, why did you use new Array? You could have done this:

const items: Item[] = [...files, ...folders]
Lauren Yim
  • 12,700
  • 2
  • 32
  • 59
  • Thank you, may i ask what is the problem of using new Array instead of [...]? – Arh Hokagi Mar 08 '20 at 13:02
  • 1
    @ArhHokagi [It's faster](https://stackoverflow.com/q/7375120/8289918) and (in my opinion) is clearer. It also helps to avoid bugs because if you invoke `new Array` with a single number (such as `new Array(2)`), it would just create an empty array with `length` 2. – Lauren Yim Mar 08 '20 at 13:10