janhommes / o.js

o.js - client side oData lib.
https://janhommes.github.io/o.js/example/
MIT License
241 stars 58 forks source link

typescript #12

Closed nmocruz closed 8 years ago

nmocruz commented 8 years ago

add support to typescript

janhommes commented 8 years ago

I am not used to typescript. I thought you can use every JS-lib in ts. What needs to be done to support typescript?

ghost commented 8 years ago

Basically "someone" has to write a Typescript Definition File for your Library as outlined in the Typescript Handbook (http://www.typescriptlang.org/Handbook#writing-dts-files). But i didn't tried that.

IceOnFire commented 8 years ago

I did, but I'm not sure it's 100% correct. It works for my basic tasks:

interface Options {
  endpoint: string
  json?: boolean
  version?: number
  strictMode?: boolean
  start?(): void
  ready?(): void
  error?(): void
  headers?: string[]
  username?: string
  password?: string
  isAsync?: boolean
}

interface OHandler {
  inlinecount: number
  data: Object[]
  config(options?: Options): OHandler
  get(callback?: (data: Object[]) => void): Q.Promise<{}>
  save(callback?: (data: Object[]) => void): Q.Promise<{}>
  route(path: string, callback?: (data: Object[]) => void)
  find(selector: string|number): OHandler
  top(quantity: number): OHandler
  take(quantity: number): OHandler
  skip(quantity: number): OHandler
  first(): OHandler
  filter(filter: string): OHandler
  where(filter: string): OHandler
  any(filter: string, resource: string): OHandler
  search(columns: string[], term: string): OHandler
  orderBy(column: string, direction?: boolean): OHandler
  orderByDesc(column: string): OHandler
  count(): OHandler
  inlineCount(paramName?: string): OHandler
  batch(resource: string): OHandler
  expand(resource: string): OHandler
  ref(resource: string, id: string|number): OHandler
  post(params: Object): OHandler
  patch(params: Object): OHandler
  put(params: Object): OHandler
  remove(params: Object): OHandler
}

declare function o(options?: string|Options): OHandler

Please have a try and let me know, we can add it to the Typings registry.

bradzacher commented 8 years ago

I created a pull request with a slightly modified version of your def, @IceOnFire

see DefinitelyTyped/DefinitelyTyped#9209

IceOnFire commented 8 years ago

Nice, thanks!

IceOnFire commented 8 years ago

I think that this issue can be closed, since the feature has been implemented and is related to another project anyway.

cbfrank commented 7 years ago

Sorry for appending more question on this closed issue. I use npm to add types/odata, and this works for below: var query = o("****");

but how should I declare an interface that contains a field of the OHandler? ` import * as odata from "odata"

interface myData {

source: odata.OHandler<T>;

} ` this doesn't work.

Thanks

janhommes commented 7 years ago

Mhh, not sure about that. Haven't used o.js with typescript. Mayb just use the source: o ? @IceOnFire any hints on this?

IceOnFire commented 7 years ago

I'm sorry, I too don't write TypeScript code.

I tried fiddling around on a sample project but came up to the conclusion that there are some issues on odata's type definition.

import * as o from 'odata'

interface Market {
  Name: string
  Description: string
}

const query = o('http://localhost:3000/Markets')

const promise: Promise<OHandler<Market>> = query.get<Market>()

promise.then((oHandler: OHandler<Market>) => {
  console.log(oHandler.data)
})

I'm quite sure that the promise should have type Promise<OHandler<Market>> because if I try with another type then TypeScript says that this is the correct one. However, the interface OHandler is not exported inside the module definition and I couldn't manage to export it correctly after some tries.

I'm afraid we need someone more skilled with TypeScript, or I just need some more time to learn it well enough.

IceOnFire commented 7 years ago

Ok this seems to work :P

import Q = require('q')

declare namespace odata {
  interface Options {
    endpoint: string
    format?: string
    autoFormat?: boolean
    version?: number
    strictMode?: boolean
    start?(): void
    ready?(): void
    error?(): void
    headers?: string[]
    username?: string
    password?: string
    isAsync?: boolean
  }

  export interface OHandler<T> {
    inlinecount: number
    data: T[]

    config<T>(options?: Options): OHandler<T>
    progress<T>(callback: () => any): OHandler<T>

    get<T>(callback?: (data: T) => void): Q.Promise<OHandler<T>>
    save<T>(callback?: (data: T) => void): Q.Promise<OHandler<T>>

    post<T>(params: any): OHandler<T>
    patch<T>(params: any): OHandler<T>
    put<T>(params: any): OHandler<T>
    remove<T>(params?: any): OHandler<T>

    routes<T>(path: string, callback?: (data: T) => void): OHandler<T>
    route<T>(path: string, callback?: (data: T) => void): OHandler<T>
    triggerRoute(hash: string): OHandler<T>
    beforeRouting(callback: (routeParams: any) => boolean): OHandler<T>

    isEndpoint(): boolean
    loading<T>(startFn: () => any | boolean, endFn: () => any): OHandler<T>

    find<T>(selector: string | number): OHandler<T>

    top<T>(quantity: number): OHandler<T>
    take<T>(quantity: number): OHandler<T>
    skip<T>(quantity: number): OHandler<T>
    first<T>(): OHandler<T>

    include<T>(column: string, data: string): OHandler<T>
    exclude<T>(column: string, data: string): OHandler<T>
    filterByList<T>(column: string, data: string): OHandler<T>

    filter<T>(filter: string): OHandler<T>
    where<T>(filter: string): OHandler<T>
    any<T>(filter: string, resource: string): OHandler<T>
    search<T>(columns: string[], term: string): OHandler<T>

    orderBy<T>(column: string, direction?: boolean): OHandler<T>
    orderByDesc<T>(column: string): OHandler<T>
    select<T>(selectStr: string): OHandler<T>

    count<T>(): OHandler<T>
    inlineCount<T>(paramName?: string): OHandler<T>

    batch<T>(resource: string): OHandler<T>
    expand<T>(resource: string): OHandler<T>
    ref<T>(resource: string, id: string | number): OHandler<T>
    removeRef<T>(resource: string, id: string | number): OHandler<T>
    deleteRef<T>(resource: string, id: string | number): OHandler<T>
  }

  export function o(options?: string | Options): OHandler<{}>
}

@janhommes could you please review it and upload it if everything seems right to you?

janhommes commented 7 years ago

Ok, so basically just export the OHandler as well? Not sure if this part export function o(options?: string | Options): OHandler<{}> would allow using the jquery like shorthand function still (o('some/url'))

cbfrank commented 7 years ago

thank you for your help, should Options be exported either?

IceOnFire commented 7 years ago

@janhommes The difference is also in the import, which is outside the module, and the module, that now is a namespace.

I'm trying with the following code and every line is giving me 0 errors:

/// <reference path="odata.d.ts" />

import * as o from 'odata'

interface Market {
  Name: string
  Description: string
}

// promise-based
const query = o('http://localhost:3000/Markets')
const promise: Promise<o.OHandler<Market>> = query.get<Market>()
promise.then((oHandler: o.OHandler<Market>) => { console.log(oHandler.data) })

// callback-based
o('http://localhost:3000/Markets').get((data: [Market]) => console.log(data))

// with config
o().config({ endpoint: 'http://localhost:3000/' })
o('Markets').get((data: [Market]) => console.log(data))

@cbfrank Options seem to work too: I just tried this without errors:

const options: o.Options = { endpoint: 'http://localhost:3000/' }
o().config(options)

Actually with the namespace definition it seems there's no need for exports, it just works under the namespace o.

janhommes commented 7 years ago

Nice. I will update the definitions for odata. Can we use namespaces for the o.js types as well? is this possible?

declare namespace 'o.js' {} 
lusid commented 7 years ago

This worked fine for me until I got tired of configuring o() everywhere and decided to put it into a service like so:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';

import { AuthenticationService } from '../core/authentication/authentication.service';
import { OHandler, Options } from 'odata/parts';
import * as o from 'odata';
import * as _ from 'lodash';

import { environment } from '../../environments/environment';

@Injectable()
export class VendorService {

  constructor(private authenticationService: AuthenticationService) {}

  odata(value: string): OHandler<{}> {
    const token = this.authenticationService.credentials.token;
    const config: Options = <Options>_.defaults({
      headers: [
        { name: 'Authorization', value: `Bearer ${token}` }
      ]
    }, environment.odata.connect);
    o().config(config);
    return o(value);
  }

}

Because I couldn't get access to Options or OHandler since they aren't publicly exported, I had to use my own mappings and separate out the type exports into their own module (as shown above) called 'odata/parts'.

The full index.d.ts I'm using looks like this now (shown below), and it works for my needs. I'm not a typescript expert so there may be a better way to handle this, but I was unable to find it. This shouldn't have any impact on those that are using it the old way since the interfaces were never exported to begin with.

// Type definitions for odata v0.3.3
// Project: https://github.com/janhommes/odata
// Definitions by: Jan Hommes <https://github.com/janhommes>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

declare module 'odata/parts' {
    import Q = require("q");

    export interface Options {
        endpoint : string
        format ?: string
        autoFormat ?: boolean
        version ?: number
        strictMode ?: boolean
        start ?: () => any
        ready ?: () => any
        error ?: () => any
        headers ?: { name: string, value: string }[]
        username ?: string
        password ?: string
        isAsync ?: boolean
    }

    export interface OHandler<T> {
        inlinecount : number
        data : T

        config<T>(options ?: Options) : OHandler<T>
        progress<T>(callback : () => any) : OHandler<T>

        get<T>(callback ?: (data : T) => void) : Q.Promise<OHandler<T>>
        save<T>(callback ?: (data : T) => void) : Q.Promise<OHandler<T>>

        post<T>(params : any) : OHandler<T>
        patch<T>(params : any) : OHandler<T>
        put<T>(params : any) : OHandler<T>
        remove<T>(params ?: any) : OHandler<T>

        routes<T>(path : string, callback ?: (data : T) => void) : OHandler<T>
        route<T>(path : string, callback ?: (data : T) => void) : OHandler<T>
        triggerRoute(hash : string) : OHandler<T>
        beforeRouting(callback : (routeParams : any) => boolean) : OHandler<T>

        isEndpoint() : boolean
        loading<T>(startFn : () => any | boolean, endFn : () => any) : OHandler<T>

        find<T>(selector : string|number) : OHandler<T>

        top<T>(quantity : number) : OHandler<T>
        take<T>(quantity : number) : OHandler<T>
        skip<T>(quantity : number) : OHandler<T>
        first<T>() : OHandler<T>

        include<T>(column : string, data : string) : OHandler<T>
        exclude<T>(column : string, data : string) : OHandler<T>
        filterByList<T>(column : string, data : string) : OHandler<T>

        filter<T>(filter : string) : OHandler<T>
        where<T>(filter : string) : OHandler<T>
        any<T>(filter : string, resource : string) : OHandler<T>
        search<T>(columns : string[], term : string) : OHandler<T>

        orderBy<T>(column : string, direction ?: boolean) : OHandler<T>
        orderByDesc<T>(column : string) : OHandler<T>
        select<T>(selectStr : string) : OHandler<T>

        count<T>() : OHandler<T>
        inlineCount<T>(paramName ?: string) : OHandler<T>

        batch<T>(resource : string) : OHandler<T>
        expand<T>(resource : string) : OHandler<T>
        ref<T>(resource : string, id : string | number) : OHandler<T>
        removeRef<T>(resource : string, id : string | number) : OHandler<T>
        deleteRef<T>(resource : string, id : string | number) : OHandler<T>
    }

    export interface OFn<T> extends OHandler<T> {
        (options ?: string | Options) : OHandler<T>
    }
}

declare module 'odata' {
    import { OFn } from 'odata/parts';

    var o : OFn<{}>;

    export = o
}