stardog.js
stardog.js copied to clipboard
Improve Typings for query.execute
It would be nice if functions like execute returned typed results. Some potential steps towards that could be:
- make the
HTTP.Bodygeneric:
type SuccessBody<T> = {
status: number;
statusText: string;
headers: Headers;
url: string
ok: true;
body: T;
}
type ErrorBody = {
status: number;
statusText: string;
headers: Headers;
url: string
ok: false;
body: {
message: string,
code: string
};
}
export type Body<T = any> = SuccessBody<T> | ErrorBody
By making Body a tagged union of success and error types, a type guard can properly narrow Body based on the ok key:
const handleResponse = (res: HTTP.Body<SomeResponseType>) => {
if (!res.ok) {
return console.error(res.body.message); // res.body is of type { message: string, code: string }
}
console.log(res.body); // res.body is SomeResponseType
};
With that in place, execute could look something like:
type RDFIRITerm = { type: 'uri', value: string }
type RDFLiteralTerm = { type: 'literal', value: string, 'xml:lang'?: string, datatype?: string }
type RDFBlankTerm = { type: 'bnode', value: string }
type RDFTerm = RDFIRITerm | RDFLiteralTerm | RDFBlankTerm
type SPARQLSelectResult<Variable extends string> = {
head: {
vars: Variable[]
link: string[]
}
results: {
bindings: Array<{ [V in Variable]: RDFTerm }>
}
}
type SPARQLAskResult = {
head: {},
boolean: boolean
}
type SPARQLResult<Variable extends string> = SPARQLSelectResult<Variable> | SPARQLAskResult
function execute<Variable extends string = ''>(
conn: Connection, database: string, query: string, accept?: HTTP.AcceptMimeType, params?: object
): Promise<HTTP.Body<SPARQLResult<Variable>>>;
and be used like
query.execute<"name" | "age">(
conn,
'myDB',
'SELECT * WHERE { :personA :name ?name ; :age ?age }',
'application/sparql-results+json'
)
.then((res) => {
if (!res.ok) {
return console.error(res.body.message);
}
console.log(res.body.head) // res.body.head is of type {} | { vars: ("name" | "age")[], link: string[], }
});
If this is of interest, I'd be happy to open a PR.
Actually, we could do better by overloading execute to better preserve the type of the response, either a SPARQL select query result or ask query result. E.g.
function execute(
conn: Connection, database: string, query: string, accept?: HTTP.AcceptMimeType, params?: object
): Promise<HTTP.Body<SPARQLResult<''>>>;
function execute<QueryType extends 'Ask'>(
conn: Connection, database: string, query: string, accept?: HTTP.AcceptMimeType, params?: object
): Promise<HTTP.Body<SPARQLAskResult>>;
function execute<QueryType extends 'Select', Variable extends string = ''>(
conn: Connection, database: string, query: string, accept?: HTTP.AcceptMimeType, params?: object
): Promise<HTTP.Body<SPARQLSelectResult<Variable>>>;
and to use:
query.execute<'Select', 'name' | 'age'>(
conn,
'myDB',
'SELECT * WHERE { :personA :name ?name ; :age ?age }',
'application/sparql-results+json'
)
.then((res) => {
if (!res.ok) {
return console.error(res.body.message);
}
res.body.results.bindings.map((result) => ( // result is of type { name: RDFTerm, age: RDFTerm }
console.log(result.name, result.age)
));
});
We're currently working on a 2.0 release of stardog.js, which may improve things here. If it doesn't, it's something that we can consider doing then. Thanks!