NEW ANSWER:
Okay, I'm switching this around: your DTO is an interface corresponding to plain-old-javascript-objects that you get from your API:
interface SourceOfAddressDto {
sourceOfAddressId?: number | undefined;
sourceOfAddressCode?: string | undefined;
sourceOfAddressDescription?: string | undefined;
sourceOfAddressLabel?: string | undefined;
createDate: Date;
userId: string;
}
And your Lookup is a bona fide class with methods that you need to use, like the shout()
one I'll make up as an example:
class SourceOfAddressLookup {
sourceOfAddressId?: number | undefined;
sourceOfAddressCode?: string | undefined;
sourceOfAddressDescription?: string | undefined;
sourceOfAddressLabel?: string | undefined;
shout() {
console.log("HELLO MY ID IS " + this.sourceOfAddressId +
" AND MY CODE IS \"" + this.sourceOfAddressCode + "\"" +
" AND MY DESCRIPTION IS \"" + this.sourceOfAddressDescription + "\"" +
" AND MY LABEL IS \"" + this.sourceOfAddressLabel + "\"!");
}
}
Instead of pluck()
from before, I'll define assignProps()
, which takes a destination object, a source object, and a list of property keys to copy from the source to the destination. It's generic so the compiler should yell at you if for some reason the source's properties are not the right type for the destination:
function assignProps<T, K extends keyof T>(
destination: T,
source: Pick<T, K>,
...keys: K[]
): T {
keys.forEach(k => destination[k] = source[k]);
return destination;
}
So now, mapFromSourceOfAddressDto
takes a SourceOfAddressDto
and calls assignProps()
on a newly constructed SourceOfAddressLookup
instance:
const mapFromSourceOfAddressDto = (dto: SourceOfAddressDto) => assignProps(
new SourceOfAddressLookup(),
dto,
"sourceOfAddressId",
"sourceOfAddressCode",
"sourceOfAddressDescription",
"sourceOfAddressLabel"
)
This compiles with no error, so the types should work. Then you can do array mapping pretty easily like this:
class Foo {
public addressSourceList: Array<SourceOfAddressLookup>;
constructor(addressSourceListDto: Array<SourceOfAddressDto>) {
this.addressSourceList = addressSourceListDto.map(mapFromSourceOfAddressDto);
}
}
And let's test it by constructing something with an array of SourceOfAddressDto
objects:
const foo = new Foo([{
createDate: new Date(),
userId: "abc",
sourceOfAddressId: 0,
sourceOfAddressDescription: "d",
sourceOfAddressCode: "c",
sourceOfAddressLabel: "l"
}, {
createDate: new Date(),
userId: "def",
sourceOfAddressId: 1,
sourceOfAddressDescription: "D",
sourceOfAddressCode: "C",
sourceOfAddressLabel: "L"
}]);
That should be mapped by Foo
's constructor to an array of SourceOfAddressLookup
instances, so let's test it by calling the shout()
method for each element:
foo.addressSourceList.forEach(x => x.shout())
// HELLO MY ID IS 0 AND MY CODE IS "c" AND MY DESCRIPTION IS "d" AND MY LABEL IS "l"!
// HELLO MY ID IS 1 AND MY CODE IS "C" AND MY DESCRIPTION IS "D" AND MY LABEL IS "L"!
Okay, looks good. Good luck again!
Playground link to code
OLD ANSWER:
I assume that your DTO is a full-fledged class with methods
class SourceOfAddressDto {
sourceOfAddressId: number | undefined;
sourceOfAddressCode: string | undefined;
sourceOfAddressDescription: string | undefined;
sourceOfAddressLabel: string | undefined;
createDate: Date;
userId: string;
constructor(createDate: Date, userId: string) {
this.createDate = createDate; this.userId = userId;
}
}
but that the lookup type can be an interface which just has some of the same properties. If you want this type to have a name you can do it this way:
// if you must name this type, you can do this:
interface SourceOfAddressLookup extends Pick<SourceOfAddressDto,
"sourceOfAddressCode" |
"sourceOfAddressDescription" |
"sourceOfAddressId" |
"sourceOfAddressLabel"
> { }
Anyway in general I'd use a function like pluck()
to take an existing object and make a new one by copying a list of properties:
function pluck<T, K extends keyof T>(t: T, ...k: K[]) {
return k.reduce((a, k) => (a[k] = t[k], a), {} as Pick<T, K>)
}
And then your mapFromSourceOfAddressDto
function can use it like this:
function mapFromSourceOfAddressDto(obj: SourceOfAddressDto): SourceOfAddressLookup {
return pluck(
obj,
"sourceOfAddressId",
"sourceOfAddressCode",
"sourceOfAddressDescription",
"sourceOfAddressLabel"
);
}
And we can make sure it works:
const dto = new SourceOfAddressDto(new Date(), "abc");
dto.sourceOfAddressCode = "cod";
dto.sourceOfAddressDescription = "descrip";
dto.sourceOfAddressLabel = "lab";
dto.sourceOfAddressId = 1;
const lookup = mapFromSourceOfAddressDto(dto);
console.log(JSON.stringify(lookup));
/* {
"sourceOfAddressId":1,
"sourceOfAddressCode":"cod",
"sourceOfAddressDescription":"descrip",
"sourceOfAddressLabel":"lab"
}
*/
Looks good to me. Hope this helps give you some direction; good luck!
Playground link to code