From f2bbad4fc727cef775d70b216e39db9599ff5dca Mon Sep 17 00:00:00 2001 From: wasyted Date: Thu, 18 Sep 2025 15:27:46 -0300 Subject: [PATCH] Typescript Capstone Challenge TypeScript Essentials Capstone Challenge Code sample consist of implementation of Observer, Observable and usage of this classes (handling requests). The assignment implies the coverage of the types of the entire code not only Observable & Observer but also request, users objects and observer handlers. Acceptance criteria The code sample should be fully covered with types. --- index.js | 127 ------------------------------------------- index.ts | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 127 deletions(-) delete mode 100644 index.js create mode 100644 index.ts diff --git a/index.js b/index.js deleted file mode 100644 index c0f3ec3..0000000 --- a/index.js +++ /dev/null @@ -1,127 +0,0 @@ -class Observer { - constructor(handlers) { - this.handlers = handlers; - this.isUnsubscribed = false; - } - - next(value) { - if (this.handlers.next && !this.isUnsubscribed) { - this.handlers.next(value); - } - } - - error(error) { - if (!this.isUnsubscribed) { - if (this.handlers.error) { - this.handlers.error(error); - } - - this.unsubscribe(); - } - } - - complete() { - if (!this.isUnsubscribed) { - if (this.handlers.complete) { - this.handlers.complete(); - } - - this.unsubscribe(); - } - } - - unsubscribe() { - this.isUnsubscribed = true; - - if (this._unsubscribe) { - this._unsubscribe(); - } - } -} - -class Observable { - constructor(subscribe) { - this._subscribe = subscribe; - } - - static from(values) { - return new Observable((observer) => { - values.forEach((value) => observer.next(value)); - - observer.complete(); - - return () => { - console.log('unsubscribed'); - }; - }); - } - - subscribe(obs) { - const observer = new Observer(obs); - - observer._unsubscribe = this._subscribe(observer); - - return ({ - unsubscribe() { - observer.unsubscribe(); - } - }); - } -} - -const HTTP_POST_METHOD = 'POST'; -const HTTP_GET_METHOD = 'GET'; - -const HTTP_STATUS_OK = 200; -const HTTP_STATUS_INTERNAL_SERVER_ERROR = 500; - - -const userMock = { - name: 'User Name', - age: 26, - roles: [ - 'user', - 'admin' - ], - createdAt: new Date(), - isDeleated: false, -}; - -const requestsMock = [ - { - method: HTTP_POST_METHOD, - host: 'service.example', - path: 'user', - body: userMock, - params: {}, - }, - { - method: HTTP_GET_METHOD, - host: 'service.example', - path: 'user', - params: { - id: '3f5h67s4s' - }, - } -]; - -const handleRequest = (request) => { - // handling of request - return {status: HTTP_STATUS_OK}; -}; -const handleError = (error) => { - // handling of error - return {status: HTTP_STATUS_INTERNAL_SERVER_ERROR}; -}; - -const handleComplete = () => console.log('complete'); - -const requests$ = Observable.from(requestsMock); - -const subscription = requests$.subscribe({ - next: handleRequest, - error: handleError, - complete: handleComplete -}); - -subscription.unsubscribe(); diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..6baec9b --- /dev/null +++ b/index.ts @@ -0,0 +1,162 @@ +// Enumerators, Types & Interfaces + +enum HTTPMethod { + POST = "POST", + GET = "GET", +} + +enum HTTPStatus { + OK = 200, + INTERNAL_SERVER_ERROR = 500, +} + +type Handlers = { + next?: (value: T) => void; + error?: (error: Error) => void; + complete?: () => void; +}; + +type User = { + name: string; + age: number; + roles: string[]; + createdAt: Date; + isDeleted: boolean; +}; + +interface IAppRequest { + method: HTTPMethod; + host: string; + path: string; + body?: User; + params: Record; +} + +interface ISubscription { + unsubscribe: () => void; +} + +// OBSERVER & OBSERVABLE with generics + +class Observer { + private handlers: Handlers; + private isUnsubscribed: boolean; + public _unsubscribe?: () => void; + + constructor(handlers: Handlers) { + this.handlers = handlers; + this.isUnsubscribed = false; + } + + next(value: T): void { + if (this.handlers.next && !this.isUnsubscribed) { + this.handlers.next(value); + } + } + + error(error: Error): void { + if (!this.isUnsubscribed) { + if (this.handlers.error) { + this.handlers.error(error); + } + this.unsubscribe(); + } + } + + complete(): void { + if (!this.isUnsubscribed) { + if (this.handlers.complete) { + this.handlers.complete(); + } + this.unsubscribe(); + } + } + + unsubscribe(): void { + this.isUnsubscribed = true; + if (this._unsubscribe) { + this._unsubscribe(); + } + } +} + +class Observable { + private _subscribe: (observer: Observer) => () => void; + + constructor(subscribe: (observer: Observer) => () => void) { + this._subscribe = subscribe; + } + + static from(values: T[]): Observable { + return new Observable((observer) => { + values.forEach((value) => observer.next(value)); + observer.complete(); + return () => { + console.log("unsubscribed"); + }; + }); + } + + subscribe(obs: Handlers): ISubscription { + const observer = new Observer(obs); + observer._unsubscribe = this._subscribe(observer); + return { + unsubscribe() { + observer.unsubscribe(); + }, + }; + } +} + +// USAGE & MOCK DATA + +// userMock Readonly to prevent accidental mutation. +const userMock: Readonly = { + name: "User Name", + age: 26, + roles: ["user", "admin"], + createdAt: new Date(), + isDeleted: false, +}; + +const requestsMock: IAppRequest[] = [ + { + method: HTTPMethod.POST, + host: "service.example", + path: "user", + body: userMock, + params: {}, + }, + { + method: HTTPMethod.GET, + host: "service.example", + path: "user", + params: { + id: "3f5h67s4s", + }, + }, +]; + +const handleRequest = ( + request: IAppRequest +): { status: HTTPStatus; request: IAppRequest } => { + console.log(`Handling request: ${request.method} ${request.path}`); + return { status: HTTPStatus.OK, request }; +}; + +const handleError = (error: Error): { status: HTTPStatus } => { + console.error(`Handling error: ${error.message}`); + return { status: HTTPStatus.INTERNAL_SERVER_ERROR }; +}; + +const handleComplete = () => console.log("complete"); + +const requests$: Observable = Observable.from(requestsMock); // The observable emits IAppRequest type values. + +const subscription: ISubscription = requests$.subscribe({ + next: handleRequest, + error: handleError, + complete: handleComplete, +}); + +subscription.unsubscribe(); \ No newline at end of file