Skip to content

Commit 007dea3

Browse files
committed
sqlite3: Add type definitions, as a .js.flow rather than libdef
This is much, much nicer for actually writing the types than a libdef would be. In particular iterating on changes is much faster and easier, because Flow does its usual incremental thing rather than restarting from scratch on each edit. More details in some new docs text in howto/libdefs.
1 parent a7d19c0 commit 007dea3

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

.flowconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ munge_underscores=true
115115

116116
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
117117
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
118+
# This lets us write .js.flow files instead of libdefs.
119+
# Add more libraries as needed to this pattern with `\|`: `foo\|bar\|…`.
120+
module.name_mapper='^\(sqlite3\)$' -> '<PROJECT_ROOT>/types/\0'
118121

119122
suppress_type=$FlowIssue
120123
suppress_type=$FlowFixMe

docs/howto/libdefs.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,39 @@ dependencies well-typed.
1717

1818
-----
1919

20+
## Normal type definitions (`.js.flow`): `sqlite3`
21+
22+
This library doesn't have its own Flow types, and doesn't have its own
23+
TypeScript types so it's not suited for Flowgen. We wrote type
24+
definitions from scratch.
25+
26+
For doing that, an option that's much more convenient than a libdef in
27+
`flow-typed` is to write a `.js.flow` file. In particular iterating
28+
on changes is much faster and easier, because Flow does its usual
29+
incremental thing; whereas with a libdef it restarts from scratch on
30+
each edit.
31+
32+
The major difference in structure between a libdef and a `.js.flow`
33+
file is that the latter doesn't have `declare module` blocks.
34+
Instead the whole file describes one module, identified by where it is
35+
in the filesystem -- just like a normal JS file does. The contents of
36+
the file are very much like the body of any given `declare module`
37+
block in a libdef: `export type Foo = …` and `declare export class
38+
Bar { … }` and so on.
39+
40+
In order to be able to write a `.js.flow` file for the library in our
41+
tree (rather than under `node_modules/`) and have Flow find it, we
42+
added the following line to `.flowconfig`:
43+
44+
module.name_mapper='^\(sqlite3\)$' -> '<PROJECT_ROOT>/types/\0'
45+
46+
Then the file goes at `types/sqlite3.js.flow`.
47+
48+
The actual contents of the `.js.flow` file were based on the library's
49+
API documentation, and consulting its implementation for points where
50+
the documentation wasn't clear.
51+
52+
2053
## `remotedev-serialize`
2154

2255
Some assembly was required for a libdef for `remotedev-serialize`, but

types/sqlite3.js.flow

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Types for the NPM package `sqlite3`.
3+
4+
Based on upstream API docs:
5+
https://github.com/mapbox/node-sqlite3/wiki/API
6+
7+
@flow strict-local
8+
*/
9+
10+
/* eslint-disable */
11+
12+
// From `Statement::BindParameter` in the implementation.
13+
// It actually also accepts arbitrary objects using ToString,
14+
// but let's say that it doesn't.
15+
export type QueryParameter = string | number | boolean | null | RegExp | Date | Buffer;
16+
17+
type Row = { ... };
18+
19+
type ResultCallback<T> = interface { (Error): void, (null, T): void };
20+
21+
declare export class Database {
22+
constructor(filename: string): this; // not modeled: mode and callback
23+
24+
close(cb?: ResultCallback<void>): void;
25+
26+
// configure(…) not modeled
27+
28+
// Does the `run` callback get a second argument? Not sure from docs.
29+
// Not modeled here: the `this` on the `run` callback.
30+
run(sql: string, params: QueryParameter[], cb?: ResultCallback<void>): this;
31+
run(sql: string, cb?: ResultCallback<void>): this;
32+
33+
get(sql: string, params: QueryParameter[], cb?: ResultCallback<Row | void>): this;
34+
get(sql: string, cb?: ResultCallback<Row | void>): this;
35+
36+
all(sql: string, params: QueryParameter[], cb?: ResultCallback<Row[]>): this;
37+
all(sql: string, cb?: ResultCallback<Row[]>): this;
38+
39+
each(
40+
sql: string,
41+
params: QueryParameter[],
42+
cb?: ResultCallback<Row>,
43+
complete?: ResultCallback<number>,
44+
): this;
45+
each(sql: string, cb?: ResultCallback<Row>, complete?: ResultCallback<number>): this;
46+
47+
exec(sql: string, cb?: ResultCallback<void>): this;
48+
49+
// Not modeled: the Statement class, and:
50+
// prepare(sql: string, params: QueryParameter[], cb?: ResultCallback<void>): Statement;
51+
// prepare(sql: string, cb?: ResultCallback<void>): Statement;
52+
53+
// The library also accepts forms one might write like:
54+
// all(sql: string, ...params: QueryParameter[], cb?: ResultCallback<Row[]>): this
55+
// with the query parameters splatted right into the arguments list.
56+
// But that means a variable-length list of arguments followed by another
57+
// argument, which the Flow type system doesn't permit. We include the
58+
// zero-argument version of that for convenience; otherwise, pass an array.
59+
//
60+
// It also accepts `params` as an object like this:
61+
// db.run("UPDATE tbl SET name = $name WHERE id = $id", { $id: 2, $name: "bar" });
62+
// We leave that out for now.
63+
}
64+
65+
export default {
66+
// `verbose` function not modeled
67+
Database,
68+
// Statement class not modeled
69+
};

0 commit comments

Comments
 (0)