typestack / class-transformer

Decorator-based transformation, serialization, and deserialization between objects and classes.
MIT License
6.76k stars 495 forks source link

plainToClass assumes response is an array #97

Open NathanHazout opened 6 years ago

NathanHazout commented 6 years ago

Given the following code:

getProposal(id: string) : Promise<Proposal> {
    const url = `${this.proposalsUrl}/${id}`;
    var headers = new Headers();
    this._authService.createAuthorizationHeader(headers);
    var options = new RequestOptions({ headers: headers });
    return this.http.get(url, options)
        .toPromise()
        .then(response => plainToClass(Proposal, response.json()));
}

I get the typescript error:

[ts] Type 'Promise<Proposal[]>' is not assignable to type 'Promise'. Type 'Proposal[]' is not assignable to type 'Proposal'. Property 'id' is missing in type 'Proposal[]'.

How did it conclude that the response is an array??

gempain commented 6 years ago

You are having this issue because Typescript does not know the type of response.json(). This is because plainToClass (and some other methods from class-transformer) has multiple signatures. From src/index.ts:

/**
 * Converts plain (literal) object to class (constructor) object. Also works with arrays.
 */
export function plainToClass<T, V extends Array<any>>(cls: ClassType<T>, plain: V, options?: ClassTransformOptions): T[];
export function plainToClass<T, V>(cls: ClassType<T>, plain: V, options?: ClassTransformOptions): T;
export function plainToClass<T, V>(cls: ClassType<T>, plain: V|V[], options?: ClassTransformOptions): T|T[] {
    return classTransformer.plainToClass(cls, plain as any, options);
}

Since Typescript cannot determine the appropriate signature, I assume it just picks the first one, hence why it's telling you that Proposal[] is expected.

To fix your problem, you simply need to explicitly declare the type of response.json() using as Proposal:

.then(response => plainToClass(Proposal, response.json() as Proposal));

And off you go :)

rightisleft commented 6 years ago

While that works - it seems a bit redundant?

davidquintard commented 5 years ago

Hi there, What about this issue?

DimiTech commented 5 years ago

Hello, I'm interested in this as well! Thanks!

jasperblues commented 5 years ago

To array:

plainToClass(Permission, <any[]>JSON.parse(response))

To single:

plainToClass(Permission, <any>JSON.parse(response))

^-- a little less redundant.

Edit: Just came here in 2024 and chose one of the higher in the thread (and slightly redundant) answers, not realizing I'd found a better way in 2019 :D