Reputation: 2734
If i have something like
array1: string[] = ['foo', 'bar'];
and an interface like
export interface MyObject { name: string; }
how can I map array1 to another array of type MyObject?
array2 : MyObject[];
array2 = array1.map(s => ...);
I thought about something like
array2 = array1.map<MyObject>(s => new MyObject(...));
Upvotes: 30
Views: 74286
Reputation: 330571
This is usually where I digress into a long lecture about the difference between types and values in TypeScript, but here I'll try to make it short: since MyObject
is only known to be a type and not a constructor value, you can't call new MyObject()
. Instead, if you want to make a value of type MyObject
, you can just use a plain old object of the right shape, such as via object literal notation:
const myObject: MyObject = {name: "Alice"}; // no error
So your map
function can be the following:
const array2: MyObject[] = array1.map(s => ({name: s})); // no error
Simple, right?
Uh, well, please note as in the comments above that the parentheses around {name: s}
are necessary, because for better or worse, JavaScript parsers interpret arrow functions of the form (x => {...})
as having a block body where the stuff inside the curly braces are statements. And when you interpret name: s
as a statement it means that name
is a label, and s
is just a string expression statement with the optional semicolon omitted. Interpreted as a function body, {name: s}
just evaluates s
and doesn't return a defined value (it is void
, which is essentially the same as undefined
), which is why you get the following weird error if you leave out the parentheses:
let array2: MyObject[] = array1.map(s => { name: s }); // error!
// Type 'void[]' is not assignable to type 'MyObject[]'.
// at runtime, array2 will be [undefined, undefined] which is not what you wanted 🙁
JavaScript parsing is a bit tricky this way. Adding the parentheses to make s => (...)
fixes it. Now the stuff after the arrow can only be interpreted as a concise body... that is, a single expression. And interpreting ({name: s})
as an expression yields the object literal that we wanted.
Yay, I avoided writing a book about types and values and somehow ended up writing a different book about expressions and statements. Oh well.
Upvotes: 58
Reputation: 2976
you can't call MyObject
as constructor because it is interface
and not class
. So an option here you can just map array1
properties as MyObject
properties and after that change type to MyObject[]
array with as
const array1: string[] = ['foo', 'bar'];
export interface MyObject { name: string; }
let array2 : MyObject[];
array2 = array1.map(v => { return {name: v}; }) as MyObject[];
or another option, you can also create MyObject
class which will implement IMyObject
interface and you can call constructor. Here is an example
const array1: string[] = ['foo', 'bar'];
export interface IMyObject { name: string; }
class MyObject implements IMyObject {
name: string;
constructor(name: string) {
this.name = name;
}
}
let array2 : IMyObject[];
array2 = array1.map(v => new MyObject(v)) as IMyObject[];
Upvotes: 12