Skip to content

Commit 234ab89

Browse files
authored
Add support to specify the type returned by query functions (#137)
* Add generics to indexable getters Add generic for indexable getters (query functions, as well as `currentFile` and similar) to specify the return type. * Add types for specifically defined fields Add utility types, accessible in [className].Typed, that allows for field-related functions to give narrower results, if the user knows a file/section/etc. has specific fields.
1 parent 8f08e23 commit 234ab89

File tree

6 files changed

+230
-18
lines changed

6 files changed

+230
-18
lines changed

datacore.api.md

Lines changed: 112 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,17 @@ export class Canvas implements Linkable, File_2, Linkbearing, Taggable, Indexabl
149149
static TYPES: string[];
150150
}
151151

152+
// @public (undocumented)
153+
export namespace Canvas {
154+
// Warning: (ae-forgotten-export) The symbol "TypedFieldbearing" needs to be exported by the entry point index.d.ts
155+
//
156+
// (undocumented)
157+
export interface Typed<Fields extends {
158+
[key in string]?: Literal;
159+
}> extends Omit<Canvas, keyof TypedFieldbearing<Fields>>, TypedFieldbearing<Fields> {
160+
}
161+
}
162+
152163
// @public
153164
export type CanvasCard = CanvasTextCard | CanvasFileCard | CanvasWebCard;
154165

@@ -218,6 +229,15 @@ export class CanvasTextCard extends BaseCanvasCard implements Linkbearing, Tagga
218229
static TYPES: string[];
219230
}
220231

232+
// @public (undocumented)
233+
export namespace CanvasTextCard {
234+
// (undocumented)
235+
export interface Typed<Fields extends {
236+
[key in string]?: Literal;
237+
}> extends Omit<CanvasTextCard, keyof TypedFieldbearing<Fields>>, TypedFieldbearing<Fields> {
238+
}
239+
}
240+
221241
// @public (undocumented)
222242
export class CanvasWebCard extends BaseCanvasCard implements Indexable {
223243
// (undocumented)
@@ -396,21 +416,21 @@ export class DatacoreApi {
396416
executeTs(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
397417
executeTsx(source: string, container: HTMLElement, component: Component | MarkdownPostProcessorContext, sourcePath: string): MarkdownRenderChild;
398418
fileLink(path: string): Link;
399-
fullquery(query: string | IndexQuery): SearchResult<Indexable>;
419+
fullquery<T extends Indexable = Indexable>(query: string | IndexQuery): SearchResult<T>;
400420
headerLink(path: string, header: string): Link;
401421
local(path: string): DatacoreLocalApi;
402422
get luxon(): typeof luxon_2;
403-
page(path: string | Link): MarkdownPage | undefined;
423+
page<T extends MarkdownPage = MarkdownPage>(path: string | Link): T | undefined;
404424
parseLink(linktext: string): Link;
405425
parseQuery(query: string | IndexQuery): IndexQuery;
406426
get preact(): typeof preact_2;
407-
query(query: string | IndexQuery): Indexable[];
427+
query<T extends Indexable = Indexable>(query: string | IndexQuery): T[];
408428
resolvePath(path: string | Link, sourcePath?: string): string;
409429
tryEvaluate(expression: string | Expression, variables?: Record<string, Literal>, sourcePath?: string): Result<Literal, string>;
410-
tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string>;
430+
tryFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<SearchResult<T>, string>;
411431
tryParseLink(linktext: string): Result<Link, string>;
412432
tryParseQuery(query: string | IndexQuery): Result<IndexQuery, string>;
413-
tryQuery(query: string | IndexQuery): Result<Indexable[], string>;
433+
tryQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<T[], string>;
414434
}
415435

416436
// @public
@@ -431,12 +451,12 @@ export class DatacoreLocalApi {
431451
coerce: typeof Coerce;
432452
get core(): Datacore;
433453
createContext: typeof preact_2.createContext;
434-
currentFile(): MarkdownPage;
454+
currentFile<T extends MarkdownPage = MarkdownPage>(): T;
435455
currentPath(): string;
436456
embed: any;
437457
evaluate(expression: string | Expression, variables?: Record<string, Literal>, sourcePath?: string): Literal;
438458
fileLink(path: string): Link;
439-
fullquery(query: string | IndexQuery): SearchResult<Indexable>;
459+
fullquery<T extends Indexable = Indexable>(query: string | IndexQuery): SearchResult<T>;
440460
Group: typeof Group;
441461
headerLink(path: string, header: string): Link;
442462
Icon: typeof Icon;
@@ -454,7 +474,7 @@ export class DatacoreLocalApi {
454474
// (undocumented)
455475
path: string;
456476
get preact(): typeof preact_2;
457-
query(query: string | IndexQuery): Indexable[];
477+
query<T extends Indexable = Indexable>(query: string | IndexQuery): T[];
458478
require(path: string | Link): Promise<unknown>;
459479
resolvePath(path: string | Link, sourcePath?: string): string;
460480
// (undocumented)
@@ -467,35 +487,35 @@ export class DatacoreLocalApi {
467487
// (undocumented)
468488
Textbox: typeof Textbox;
469489
tryEvaluate(expression: string | Expression, variables?: Record<string, Literal>, sourcePath?: string): Result<Literal, string>;
470-
tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string>;
490+
tryFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<SearchResult<T>, string>;
471491
tryParseLink(linktext: string): Result<Link, string>;
472492
tryParseQuery(query: string | IndexQuery): Result<IndexQuery, string>;
473-
tryQuery(query: string | IndexQuery): Result<Indexable[], string>;
493+
tryQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<T[], string>;
474494
useArray<T, U>(input: T[] | DataArray<T>, process: (data: DataArray<T>) => DataArray<U>, deps?: unknown[]): U[];
475495
useCallback: typeof hooks.useCallback;
476496
useContext: typeof hooks.useContext;
477-
useCurrentFile(settings?: {
497+
useCurrentFile<T extends MarkdownPage = MarkdownPage>(settings?: {
478498
debounce?: number;
479-
}): MarkdownPage;
499+
}): T;
480500
useCurrentPath(settings?: {
481501
debounce?: number;
482502
}): string;
483503
useEffect: typeof hooks.useEffect;
484-
useFile(path: string, settings?: {
504+
useFile<T extends Indexable = Indexable>(path: string, settings?: {
485505
debounce?: number;
486-
}): Indexable | undefined;
487-
useFullQuery(query: string | IndexQuery, settings?: {
506+
}): T | undefined;
507+
useFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery, settings?: {
488508
debounce?: number;
489-
}): SearchResult<Indexable>;
509+
}): SearchResult<T>;
490510
useIndexUpdates(settings?: {
491511
debounce?: number;
492512
}): number;
493513
// Warning: (ae-forgotten-export) The symbol "useInterning" needs to be exported by the entry point index.d.ts
494514
useInterning: typeof useInterning;
495515
useMemo: typeof hooks.useMemo;
496-
useQuery(query: string | IndexQuery, settings?: {
516+
useQuery<T extends Indexable = Indexable>(query: string | IndexQuery, settings?: {
497517
debounce?: number;
498-
}): Indexable[];
518+
}): T[];
499519
useReducer: typeof hooks.useReducer;
500520
useRef: typeof hooks.useRef;
501521
useState: typeof hooks.useState;
@@ -1133,6 +1153,17 @@ export class MarkdownBlock implements Indexable, Linkbearing, Taggable, Fieldbea
11331153
value(key: string): Literal | undefined;
11341154
}
11351155

1156+
// @public (undocumented)
1157+
export namespace MarkdownBlock {
1158+
// Warning: (ae-forgotten-export) The symbol "TypedValuebearing" needs to be exported by the entry point index.d.ts
1159+
//
1160+
// (undocumented)
1161+
export interface Typed<Fields extends {
1162+
[key in string]?: Literal;
1163+
}> extends Omit<MarkdownBlock, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1164+
}
1165+
}
1166+
11361167
// @public
11371168
export class MarkdownCodeblock extends MarkdownBlock implements Indexable, Fieldbearing, Linkbearing {
11381169
// (undocumented)
@@ -1164,6 +1195,15 @@ export class MarkdownCodeblock extends MarkdownBlock implements Indexable, Field
11641195
value(key: string): Literal | undefined;
11651196
}
11661197

1198+
// @public (undocumented)
1199+
export namespace MarkdownCodeblock {
1200+
// (undocumented)
1201+
export interface Typed<Fields extends {
1202+
[key in string]?: Literal;
1203+
}> extends Omit<MarkdownCodeblock, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1204+
}
1205+
}
1206+
11671207
// @public
11681208
export class MarkdownDatablock extends MarkdownBlock implements Indexable, Fieldbearing, Linkbearing {
11691209
// (undocumented)
@@ -1189,6 +1229,15 @@ export class MarkdownDatablock extends MarkdownBlock implements Indexable, Field
11891229
value(key: string): Literal | undefined;
11901230
}
11911231

1232+
// @public (undocumented)
1233+
export namespace MarkdownDatablock {
1234+
// (undocumented)
1235+
export interface Typed<Fields extends {
1236+
[key in string]?: Literal;
1237+
}> extends Omit<MarkdownDatablock, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1238+
}
1239+
}
1240+
11921241
// @public
11931242
export class MarkdownListBlock extends MarkdownBlock implements Taggable, Linkbearing {
11941243
$elements: MarkdownListItem[];
@@ -1207,6 +1256,15 @@ export class MarkdownListBlock extends MarkdownBlock implements Taggable, Linkbe
12071256
static TYPES: string[];
12081257
}
12091258

1259+
// @public (undocumented)
1260+
export namespace MarkdownListBlock {
1261+
// (undocumented)
1262+
export interface Typed<Fields extends {
1263+
[key in string]?: Literal;
1264+
}> extends Omit<MarkdownListBlock, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1265+
}
1266+
}
1267+
12101268
// @public
12111269
export class MarkdownListItem implements Indexable, Linkbearing, Taggable, Fieldbearing {
12121270
$blockId?: string;
@@ -1246,6 +1304,15 @@ export class MarkdownListItem implements Indexable, Linkbearing, Taggable, Field
12461304
value(key: string): Literal | undefined;
12471305
}
12481306

1307+
// @public (undocumented)
1308+
export namespace MarkdownListItem {
1309+
// (undocumented)
1310+
export interface Typed<Fields extends {
1311+
[key in string]?: Literal;
1312+
}> extends Omit<MarkdownListItem, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1313+
}
1314+
}
1315+
12491316
// @public
12501317
export class MarkdownPage implements File_2, Linkbearing, Taggable, Indexable, Fieldbearing {
12511318
$ctime: DateTime;
@@ -1282,6 +1349,15 @@ export class MarkdownPage implements File_2, Linkbearing, Taggable, Indexable, F
12821349
value(key: string): Literal | undefined;
12831350
}
12841351

1352+
// @public (undocumented)
1353+
export namespace MarkdownPage {
1354+
// (undocumented)
1355+
export interface Typed<Fields extends {
1356+
[key in string]?: Literal;
1357+
}> extends Omit<MarkdownPage, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1358+
}
1359+
}
1360+
12851361
// @public
12861362
export class MarkdownSection implements Indexable, Taggable, Linkable, Linkbearing, Fieldbearing {
12871363
$blocks: MarkdownBlock[];
@@ -1316,6 +1392,15 @@ export class MarkdownSection implements Indexable, Taggable, Linkable, Linkbeari
13161392
value(key: string): Literal | undefined;
13171393
}
13181394

1395+
// @public (undocumented)
1396+
export namespace MarkdownSection {
1397+
// (undocumented)
1398+
export interface Typed<Fields extends {
1399+
[key in string]?: Literal;
1400+
}> extends Omit<MarkdownSection, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1401+
}
1402+
}
1403+
13191404
// @public
13201405
export class MarkdownTaskItem extends MarkdownListItem implements Indexable, Linkbearing, Taggable, Fieldbearing {
13211406
get $completed(): boolean;
@@ -1335,6 +1420,15 @@ export class MarkdownTaskItem extends MarkdownListItem implements Indexable, Lin
13351420
static TYPES: string[];
13361421
}
13371422

1423+
// @public (undocumented)
1424+
export namespace MarkdownTaskItem {
1425+
// (undocumented)
1426+
export interface Typed<Fields extends {
1427+
[key in string]?: Literal;
1428+
}> extends Omit<MarkdownTaskItem, keyof TypedValuebearing<Fields>>, TypedValuebearing<Fields> {
1429+
}
1430+
}
1431+
13381432
// @public
13391433
export interface MethodExpression {
13401434
arguments: Expression[];

src/api/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,28 +57,33 @@ export class DatacoreApi {
5757
/////////////////////////
5858

5959
/** Load a markdown file by full path or link. */
60+
public page<T extends MarkdownPage = MarkdownPage>(path: string | Link): T | undefined;
6061
public page(path: string | Link): MarkdownPage | undefined {
6162
const realPath = path instanceof Link ? path.path : path;
6263

6364
return this.core.datastore.load(realPath) as MarkdownPage | undefined;
6465
}
6566

6667
/** Execute a textual or typed index query, returning all results. */
68+
public query<T extends Indexable = Indexable>(query: string | IndexQuery): T[];
6769
public query(query: string | IndexQuery): Indexable[] {
6870
return this.tryQuery(query).orElseThrow();
6971
}
7072

7173
/** Execute a textual or typed index query, returning all results. */
74+
public tryQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<T[], string>;
7275
public tryQuery(query: string | IndexQuery): Result<Indexable[], string> {
7376
return this.tryFullQuery(query).map((result) => result.results);
7477
}
7578

7679
/** Execute a textual or typed index query, returning results plus performance metadata. */
80+
public fullquery<T extends Indexable = Indexable>(query: string | IndexQuery): SearchResult<T>;
7781
public fullquery(query: string | IndexQuery): SearchResult<Indexable> {
7882
return this.tryFullQuery(query).orElseThrow();
7983
}
8084

8185
/** Execute a textual or typed index query, returning results plus performance metadata. */
86+
public tryFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<SearchResult<T>, string>;
8287
public tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string> {
8388
const parsedQuery = typeof query === "string" ? QUERY.query.tryParse(query) : query;
8489
return this.core.datastore.search(parsedQuery);

src/api/local-api.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export class DatacoreLocalApi {
4747
}
4848

4949
/** The full markdown file metadata for the current file. */
50+
public currentFile<T extends MarkdownPage = MarkdownPage>(): T;
5051
public currentFile(): MarkdownPage {
5152
return this.api.page(this.path)!;
5253
}
@@ -167,21 +168,25 @@ export class DatacoreLocalApi {
167168
}
168169

169170
/** Execute a textual or typed index query, returning all results. */
171+
public query<T extends Indexable = Indexable>(query: string | IndexQuery): T[];
170172
public query(query: string | IndexQuery): Indexable[] {
171173
return this.api.query(query);
172174
}
173175

174176
/** Execute a textual or typed index query, returning all results. */
177+
public tryQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<T[], string>;
175178
public tryQuery(query: string | IndexQuery): Result<Indexable[], string> {
176179
return this.api.tryQuery(query);
177180
}
178181

179182
/** Execute a textual or typed index query, returning results plus performance metadata. */
183+
public fullquery<T extends Indexable = Indexable>(query: string | IndexQuery): SearchResult<T>;
180184
public fullquery(query: string | IndexQuery): SearchResult<Indexable> {
181185
return this.api.fullquery(query);
182186
}
183187

184188
/** Execute a textual or typed index query, returning results plus performance metadata. */
189+
public tryFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<SearchResult<T>, string>;
185190
public tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string> {
186191
return this.api.tryFullQuery(query);
187192
}
@@ -225,6 +230,7 @@ export class DatacoreLocalApi {
225230
}
226231

227232
/** Use the file metadata for the current file. Automatically updates the view when the current file metadata changes. */
233+
public useCurrentFile<T extends MarkdownPage = MarkdownPage>(settings?: { debounce?: number }): T;
228234
public useCurrentFile(settings?: { debounce?: number }): MarkdownPage {
229235
return useFileMetadata(this.core, this.path, settings) as MarkdownPage;
230236
}
@@ -235,6 +241,7 @@ export class DatacoreLocalApi {
235241
}
236242

237243
/** Use the file metadata for a specific file. Automatically updates the view when the file changes. */
244+
public useFile<T extends Indexable = Indexable>(path: string, settings?: { debounce?: number }): T | undefined;
238245
public useFile(path: string, settings?: { debounce?: number }): Indexable | undefined {
239246
return useFileMetadata(this.core, path, settings);
240247
}
@@ -248,11 +255,16 @@ export class DatacoreLocalApi {
248255
* Run a query, automatically re-running it whenever the vault changes. Returns more information about the query
249256
* execution, such as index revision and total search duration.
250257
*/
258+
public useFullQuery<T extends Indexable = Indexable>(
259+
query: string | IndexQuery,
260+
settings?: { debounce?: number }
261+
): SearchResult<T>;
251262
public useFullQuery(query: string | IndexQuery, settings?: { debounce?: number }): SearchResult<Indexable> {
252263
return useFullQuery(this.core, this.parseQuery(query), settings);
253264
}
254265

255266
/** Run a query, automatically re-running it whenever the vault changes. */
267+
public useQuery<T extends Indexable = Indexable>(query: string | IndexQuery, settings?: { debounce?: number }): T[];
256268
public useQuery(query: string | IndexQuery, settings?: { debounce?: number }): Indexable[] {
257269
// Hooks need to be called in a consistent order, so we don't nest the `useQuery` call in the DataArray.wrap _just_ in case.
258270
return useQuery(this.core, this.parseQuery(query), settings);

src/index/types/canvas.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {
2626
import { InlineField, jsonInlineField, valueInlineField } from "index/import/inline-field";
2727
import { File } from "index/types/indexable";
2828
import { mapObjectValues } from "utils/data";
29+
import { Literal } from "expression/literal";
30+
import { TypedFieldbearing } from "./typed-field";
2931

3032
/** A canvas file, consisting of a set of canvas cards. */
3133
export class Canvas implements Linkable, File, Linkbearing, Taggable, Indexable, Fieldbearing {
@@ -123,6 +125,13 @@ export class Canvas implements Linkable, File, Linkbearing, Taggable, Indexable,
123125
);
124126
}
125127

128+
/** @public */
129+
export namespace Canvas {
130+
export interface Typed<Fields extends { [key in string]?: Literal }>
131+
extends Omit<Canvas, keyof TypedFieldbearing<Fields>>,
132+
TypedFieldbearing<Fields> {}
133+
}
134+
126135
/** All supported canvas card types. */
127136
export type CanvasCard = CanvasTextCard | CanvasFileCard | CanvasWebCard;
128137

@@ -233,6 +242,13 @@ export class CanvasTextCard extends BaseCanvasCard implements Linkbearing, Tagga
233242
);
234243
}
235244

245+
/** @public */
246+
export namespace CanvasTextCard {
247+
export interface Typed<Fields extends { [key in string]?: Literal }>
248+
extends Omit<CanvasTextCard, keyof TypedFieldbearing<Fields>>,
249+
TypedFieldbearing<Fields> {}
250+
}
251+
236252
/** Canvas card that is just a file embedding. */
237253
export class CanvasFileCard extends BaseCanvasCard implements Indexable {
238254
static TYPES = ["canvas-card", "markdown", "canvas-file-card", TAGGABLE_TYPE, LINKABLE_TYPE, FIELDBEARING_TYPE];

0 commit comments

Comments
 (0)