Búsqueda de sitios web

HTTP: Aserción de tipo de objeto JSON a Typescript


He estado leyendo sobre interfaces y afirmación de tipos. He estado mirando páginas como:

  • Typescript analizar JSON con clase e interfaz
  • ¿Cómo convierto un objeto json a una clase de TypeScript (¡esto no tiene nada que ver con TS!)
  • Analizar objetos json complejos con Typescript
  • Documento TS sobre interfaces

Estoy empezando a entenderlo, y es bastante sencillo comprender los conceptos básicos. Aunque: en ninguna parte encuentro cómo escribir un objeto JSON a un objeto que uso en mi aplicación, cuando se trata de HTTP.

Por ejemplo, tengo un Array de objetos Teacher. Cada Teacher tiene un id, nombre y un Array de Students. Tengo otra clase Students que contiene sus atributos..... etc.

En algún lugar de esos enlaces leí que, a menos que necesites realizar acciones a un objeto, es suficiente con tener solo una Interfaz. ¿Pero a menos que quieras realizar acciones al Objeto necesitas una clase separada?

Mi clase actual de Profesor comienza con... :

export class Teacher {

    private students: Array<Student>;

    constructor(public id: string, public name: string) {
        this.students = new Array<Student>();
    }

    public getStudents(): Array<Student> {
        return this.students;
    }

}

Primero que nada, ¿cómo se vería el código si quiero convertir (o afirmar el tipo) del objeto JS a un objeto Teacher?

Ahora mi código se ve algo así:

Servicio :

getTeachers() {
    return this.http.get('someUrl')
    .map((res: Response) => res.json())
}

Componente (Componente de Angular 2):

export class ListComponent implements OnActivate {

id: string;
name: string;
teachers: Teacher[];

constructor(public _service: Service, public _router: Router) {

}

routerOnActivate(): void {
    this._service.getTeachers()
        .subscribe(teachers => this.teachers = teachers);
}

Mi interfaz se vería así:

export interface TeacherJSON {
        id: string,
        name: string,
        students: Array<Student>;
}

Si la interfaz anterior no es suficiente para poder realizar acciones sobre el objeto, ¿cómo avanzo? Aprendí que puedes agregar métodos a tu interfaz como:

interface Thing {
    a: number;
    b: string;
    foo(s: string, n: number): string;
}

process(x: Thing) {
    return x.foo("abc");
}

Entiendo el ejemplo anterior, pero supongo que el http, el mapeo y la suscripción me están confundiendo, ¡y no sé cómo implementarlo en mi código!

Respuesta #1

Entonces obviamente necesito primero cambiar mi arreglo de profesores en el componente a una instancia de mi interfaz?

No existe tal cosa como una instancia de la interfaz. Las interfaces son abstractas, existen solo en TypeScript, ni siquiera se compilan a JavaScript. Las escribes para obtener autocompletado y otras ventajas en tu editor; para ayudarte en aplicaciones grandes, de modo que no tengas que recordar qué propiedades y métodos tiene cada objeto.

Cuando tienes que instanciar un objeto, las interfaces no pueden hacer eso; usarás una clase y crearás propiedades y métodos como de costumbre. La manera correcta de hacer esto en tu ejemplo sería:

interface ITeacher{
  id: string,
  name: string,
  students: Array<Student>;
};
class Teacher implements ITeacher {
  id: string,
  name: string,
  students: Array<Student>;
}

En tu caso, implementar una interfaz no es necesario, TypeScript es lo suficientemente inteligente como para extraer la información de las clases también, por lo que estos serían iguales:

teachers: Teacher[];
teachers: ITeacher[];

Crear una interfaz tendría sentido si tienes varios tipos de profesores y quisieras asegurarte de que cada uno tenga todas las propiedades/métodos necesarios:

class FirstGradeTeacher implements ITeacher{}
class SecondGradeTeacher implements ITeacher{}

Es posible que hayas notado que no he mencionado JSON en absoluto. ¡Olvídalo! ...cuando trabajas con tus modelos de objetos (clases o interfaces). Es solo un formato de datos para tus modelos lógicos. Cuando estás estructurando tus modelos y planeando cómo deberían funcionar, no te importa sobre protocolos y formatos (el servicio HTTP se encarga de eso).

getTeachers(): Observable<Teacher> {
  return this.http.get('someUrl')
    .map((res: Response) => res.json())
}

Este código es cómo utilizarías una interfaz. Simplemente le dices a TypeScript que cuando recibas una respuesta de someUrl y la analices como json, esperas que los datos sean del tipo Teacher.

Luego, en otros lugares, cuando te subscribe() a él, puede decirte que el objeto que estás recibiendo tiene las propiedades id, name y students:

this._service.getTeachers()
  .subscribe((teachers: Teacher[]) => this.teachers = teachers);

Espero que esto ayude (:

Respuesta #2

Suponiendo que estás obteniendo la lista de profesores vía HTTP, puedes filtrarla para obtener el que necesitas:

getById(id: string) {
    return this.http.get('someUrl' + id)
        .map((res: Response) => res.json())
        .filter(teacher => teacher.id === id));