-
Notifications
You must be signed in to change notification settings - Fork 131
/
DeclarationReferences.ts
557 lines (497 loc) · 17.2 KB
/
DeclarationReferences.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
// TSDoc "declaration references" provide a straightforward and unambiguous notation
// for referencing other API declarations such as classes, member functions, enum values,
// etc. It is used with standard TSDoc tags such as {@link} and {@inheritDoc}.
//
// The full declaration reference syntax supports declarations that are imported from
// another NPM package, optionally with an NPM scope, optionally with an explicit
// import path.
//
// For example:
//
// /**
// * {@link @my-scope/my-package/path1/path2#namespace1.namespace2.MyClass.myMember
// * | a complex example}
// *
// * {@link ./lib/controls/Button#Button | referencing a local *.d.ts file}
// */
//
// The optional components to the left of the "#" mostly follow the standard rules of
// a TypeScript "import" definition, so we don't discuss them further in this sample file.
//
// TSDoc declaration references are always resolved relative to a specific entry point
// (NOT relative to the current source file or declaration scope). Thus, their syntax
// is independent of where the reference occurs within a given package. For example,
// when referring to "MyClass.methodA" below, we may want to shorten it to "methodA"
// (since the meaning seems clear from context), but TSDoc standard does not allow that.
// "MyClass.methodA" is the shortest allowable name:
//
// export class MyClass {
// public methodA(): void {
// }
//
// /**
// * CORRECT: {@link MyClass.methodA}
// * NOT CORRECT: {@link methodA}
// */
// public methodB(): void {
// }
// }
//
// Requiring fully scoped names ensures that documentation processors can resolve links
// efficiently and without access to a compiler analysis. (And for this document, it means
// we only need to give examples of simple self-references, since cross-references would
// use identical notation.)
//
// The "(:)" operator uses a "selector" after the colon. It has these properties:
//
// - It is used in the absence of a TypeScript name, or to choose between things that have
// the same name.
//
// - For members of classes, the system-defined selectors are "instance" and "static"
//
// - For members of interfaces and enums, there are no system-defined selectors.
//
// - For merged declarations, the system-defined selectors are "class", "enum", "function",
// "interface", "namespace", "type", or "variable"
//
// - Class constructors use a special "constructor" selector that applies to the class itself.
//
// - Label selectors refer to declarations indicated using the {@label LABEL} tag. The label
// must be all capitals (e.g. "WITH_NUMBERS") to avoid conflicts with system selectors.
//---------------------------------------------------------
// Static vs instance members
/**
* Shortest name: {@link ClassA1}
* Full name: {@link (ClassA1:class)}
*/
export class ClassA1 {
/**
* Shortest name: {@link ClassA1.memberA2}
* Full name: {@link (ClassA1:class).(memberA2:instance)}
*/
public memberA2(): void {}
/**
* Shortest name: {@link ClassA1.(memberA3:instance)}
* Full name: {@link (ClassA1:class).(memberA3:instance)}
*
* NOTE: The shortest name cannot omit "instance" because there is a static member
* with the same name.
*/
public memberA3(): void {}
/**
* Shortest name: {@link ClassA1.(memberA3:static)}
* Full name: {@link (ClassA1:class).(memberA3:static)}
*/
public static memberA3(): void {}
/**
* Shortest name: {@link (ClassA1:constructor)}
* Full name: {@link (ClassA1:constructor)}
*
* NOTE: "ClassA1.constructor" is NOT correct. That would refer to a regular
* member whose name is "constructor".
*/
public constructor() {
console.log('Constructed ClassA1');
}
/**
* Shortest name: {@link (ClassA1:class).constructor}
* Also valid: {@link (ClassA1:class)."constructor"}
* Full name: {@link (ClassA1:class).(constructor:instance)}
*
* NOTE: This is NOT the class constructor, but rather a property
* whose name confusingly uses a keyword.
*/
public get constructor(): string {
return 'hello';
}
}
//---------------------------------------------------------
// Nesting namespaces
/**
* Shortest name: {@link B1.B2.B3}
* Full name: {@link (B1:namespace).(B2:namespace).(B3:namespace)S}
*/
export namespace B1.B2.B3 {
/**
* Shortest name: {@link B1.B2.B3.functionB4}
* Full name: {@link (B1:namespace).(B2:namespace).(B3:namespace).(functionB4:function)}
*/
export function functionB4(): void {}
}
//---------------------------------------------------------
// Function overloads using indexes (NOT RECOMMENDED)
//
// Generally we recommend to define labels, as shown with functionD1() below.
// Numeric indexes should only be used e.g. if you are consuming a library that
// doesn't define labels, and you cannot easily fix that library.
/**
* Shortest name: {@link (functionC1:1)}
* Full name: {@link (functionC1:1)}
*/
export function functionC1(y: number): number;
/**
* Shortest name: {@link (functionC1:2)}
* Full name: {@link (functionC1:2)}
*/
export function functionC1(x: string): string;
// (MUST NOT have TSDoc, because this is the overload implementation)
export function functionC1(xy: string | number): string | number {
return '';
}
//---------------------------------------------------------
// Function overloads using labels
/**
* Shortest name: {@link (functionD1:WITH_NUMBERS)}
* Full name: {@link (functionD1:WITH_NUMBERS)}
*
* {@label WITH_NUMBERS}
*/
export function functionD1(y: number): number;
/**
* Shortest name: {@link (functionD1:WITH_LETTERS)}
* Full name: {@link (functionD1:WITH_LETTERS)}
*
* {@label WITH_LETTERS}
*/
export function functionD1(x: string): string;
/**
* Shortest name: {@link (functionD1:3)}
* Full name: {@link (functionD1:3)}
*
* NOTE: If one label is omitted, the numeric indexers can still be used.
*/
export function functionD1(): string;
// (MUST NOT have TSDoc, because this is the overload implementation)
export function functionD1(xy?: string | number): string | number {
return '';
}
//---------------------------------------------------------
// Merged declarations
/**
* Shortest name: {@link (MergedE1:class)}
* Full name: {@link (MergedE1:class)}
*/
export class MergedE1 {
/**
* Shortest name: {@link (MergedE1:constructor)}
* Full name: {@link (MergedE1:constructor)}
*
* NOTE: MergedE1 is also a namespace, so it seems like we need a syntax like
* `(MergedE1:class,constructor)` or `(MergedE1:class:constructor)`.
* But only one selector is necessary because namespaces conveniently cannot
* have constructors.
*/
public constructor() {
console.log('Constructed MergedE1 class');
}
/**
* Shortest name: {@link (MergedE1:class).memberE2}
* Full name: {@link (MergedE1:class).(memberE2:instance)}
*
* NOTES:
*
* - The "instance" selector is optional because "(MergedE1:class)" already
* eliminates any ambiguity.
*
* - Although "MergedE1.(memberE2:instance)" is theoretically also an unambiguous notation,
* the TSDoc standard discourages that, because resolving it might require
* unbounded backtracking.
*/
public memberE2(): void {}
}
/**
* Shortest name: {@link (MergedE1:namespace)}
* Full name: {@link (MergedE1:namespace)}
*/
export namespace MergedE1 {
/**
* Shortest name: {@link (MergedE1:namespace).memberE2}
* Full name: {@link (MergedE1:namespace).(memberE2:function)}
*/
export function memberE2(): void {}
}
//---------------------------------------------------------
// Merged declarations with function overloads
/**
* Shortest name: {@link (MergedF1:WITH_NUMBERS)}
* Full name: {@link (MergedF1:WITH_NUMBERS)}
*
* {@label WITH_NUMBERS}
*/
export function MergedF1(y: number): number;
/**
* Shortest name: {@link (MergedF1:2)}
* Full name: {@link (MergedF1:2)}
*/
export function MergedF1(x: string): string;
// (MUST NOT have TSDoc, because this is the overload implementation)
export function MergedF1(xy: string | number): string | number {
return '';
}
/**
* Shortest name: {@link (MergedF1:namespace)}
* Full name: {@link (MergedF1:namespace)}
*/
export namespace MergedF1 {}
//---------------------------------------------------------
// Merged declarations with extension of the same thing
/**
* Shortest name: {@link (MergedG1:interface)}
* Full name: {@link (MergedG1:interface)}
*/
export interface MergedG1 {
/**
* Shortest name: {@link (MergedG1:interface).mergedG2}
* Full name: {@link (MergedG1:interface).mergedG2}
*
* NOTE: The full name doesn't have an additional selector, because interface
* members are unambiguous (except for operators and function overloads, which
* use labels).
*/
mergedG2: string;
}
// (MUST NOT have TSDoc, because this is part of an interface that was already
// documented above.)
export interface MergedG1 {
/**
* Shortest name: {@link (MergedG1:interface).mergedG3}
* Full name: {@link (MergedG1:interface).mergedG3}
*/
mergedG3: string;
}
/**
* Shortest name: {@link (MergedG1:namespace)}
* Full name: {@link (MergedG1:namespace)}
*/
export namespace MergedG1 {
/**
* Shortest name: {@link (MergedG1:namespace).mergedG2}
* Full name: {@link (MergedG1:namespace).mergedG2}
*/
export let mergedG2: string = '';
}
// (MUST NOT have TSDoc, because this is part of a namespace that was already
// documented above.)
export namespace MergedG1 {
/**
* Shortest name: {@link (MergedG1:namespace).mergedG3}
* Full name: {@link (MergedG1:namespace).mergedG3}
*/
export let mergedG3: string = '';
}
//---------------------------------------------------------
// Enum members
/**
* Shortest name: {@link EnumH1}
* Full name: {@link (EnumH1:enum)}
*/
export const enum EnumH1 {
/**
* Shortest name: {@link EnumH1.memberH2}
* Full name: {@link (EnumH1:enum).memberH2}
*/
memberH2
}
// (MUST NOT have TSDoc, because this is part of an enum that was already
// documented above.)
export const enum EnumH1 {
/**
* Shortest name: {@link EnumH1.memberH3}
* Full name: {@link (EnumH1:enum).memberH3}
*/
memberH3 = 3
}
//---------------------------------------------------------
// Property getters/setters
/**
* Shortest name: {@link ClassI1}
* Full name: {@link (ClassI1:class)}
*/
export class ClassI1 {
private _title: string;
/**
* Shortest name: {@link ClassI1.title}
* Full name: {@link (ClassI1:class).(title:instance)}
*/
public get title(): string {
return this._title;
}
// (MUST NOT have TSDoc, because this is part of a property that was already
// documented above.)
public set title(value: string) {
this._title = value;
}
}
//---------------------------------------------------------
// Malformed names
//
// These cases seem exotic, but they often arise when declaring a TypeScript interface
// that describes a REST response.
/**
* Shortest name: {@link InterfaceJ1}
* Full name: {@link (InterfaceJ1:interface)}
*/
export interface InterfaceJ1 {
/**
* Shortest name: {@link InterfaceJ1."abc. def"}
* Full name: {@link (InterfaceJ1:interface)."abc. def"}
*/
'abc. def': string;
/**
* Shortest name: {@link InterfaceJ1."\"'"}
* Full name: {@link (InterfaceJ1:interface)."\"'"}
*
* Here the declaration references use double quotes, whereas the TypeScript uses
* single quotes, so the backslash gets swapped.
*/
'"\'': string;
/**
* Shortest name: {@link InterfaceJ1."{\\}"}
* Full name: {@link (InterfaceJ1:interface)."\"{\\}"}
*
* This example has problematic characters that affect two different encoding layers.
* The original string is `{\}`. Declaration reference notation uses double quotes and
* backslash escaping, so the quoted string becomes `"{\\}"`. The declaration reference
* itself is thus `InterfaceJ1."{\\}"`. But then we embed it in a `{@link}` tag,
* which uses `}` as a delimiter and HTML character entities for escaping. This changes
* the curly braces to `{` and `}`.
*/
'{\\}': string;
/**
* Shortest name: {@link InterfaceJ1."©"}
* Full name: {@link (InterfaceJ1:interface)."©"}
*
* Markdown supports HTML character entities, so TSDoc also supports them. In this example
* we need to escape the ampersand to avoid misinterpreting the string as a copyright symbol.
*/
'©': string;
/**
* Shortest name: {@link InterfaceJ1."1.5"}
* Full name: {@link (InterfaceJ1:interface)."1.5"}
*
* Note that the actual JavaScript object key will become a string, so "1.5" is a correct
* way to reference this item. This is a bad practice that nobody should be using, but
* our notation handles it just fine.
*/
1.5: string; //a number as the key
}
/**
* Shortest name: {@link ClassJ2}
* Full name: {@link (ClassJ2:class)}
*/
export class ClassJ2 {
/**
* Shortest name: {@link ClassJ2.static}
* Also valid: {@link ClassJ2."static"}
* Full name: {@link (ClassJ2:class).(static:static)}
*/
public static static: string = 'static member using keyword as name';
/**
* Shortest name: {@link ClassJ2."𠮷"}
* Full name: {@link (ClassJ2:class).("𠮷":instance)}
*
* NOTE: In TypeScript some characters require quotes, some do not.
* TSDoc should follow the same rules as TypeScript in this regard.
*/
public '𠮷': string = 'instance member using a Unicode surrogate pair';
/**
* Shortest name: {@link ClassJ2.spaß}
* Full name: {@link (ClassJ2:class).(spaß:instance)}
*/
public spaß: string = 'international characters that do not require quotes';
}
//---------------------------------------------------------
// Generic parameters are ignored by the notation.
/**
* Shortest name: {@link TypeK1}
* Full name: {@link (TypeK1:type)}
*
* Note that `<T>` is never part of the declaration reference notation.
* In the TypeScript language, signatures cannot be distinguished by generic parameters.
*/
export type TypeK1<T> = T | Error;
//---------------------------------------------------------
// Operators must always be selected using user-defined labels.
/**
* Shortest name: {@link InterfaceL1}
* Full name: {@link (InterfaceL1:interface)}
*/
export interface InterfaceL1 {
/**
* Shortest name: {@link InterfaceL1.(:STRING_INDEXER)}
* Full name: {@link (InterfaceL1:interface).(:STRING_INDEXER)}
*
* {@label STRING_INDEXER}
*/
[key: string]: number;
/**
* Shortest name: {@link InterfaceL1.(:NUMBER_INDEXER)}
* Full name: {@link (InterfaceL1:interface).(:NUMBER_INDEXER)}
*
* {@label NUMBER_INDEXER}
*/
[key: number]: number;
/**
* Shortest name: {@link InterfaceL1.(:FUNCTOR)}
* Full name: {@link (InterfaceL1:interface).(:FUNCTOR)}
*
* {@label FUNCTOR}
*/
(source: string, subString: string): boolean;
/**
* Shortest name: {@link InterfaceL1.(:CONSTRUCTOR)}
* Full name: {@link (InterfaceL1:interface).(:CONSTRUCTOR)}
*
* {@label CONSTRUCTOR}
*/
new (hour: number, minute: number);
}
//---------------------------------------------------------
// ECMAScript 6 introduces symbols, which are runtime objects that get mapped into
// the compile-time type system only when they follow stereotypical patterns that
// can be detected by the compiler. A declaration reference uses the "[]" operator
// to reference a symbol. It surrounds an inner declaration reference that must refer
// to variable declaration that is a unique symbol.
/**
* Shortest name: {@link WellknownSymbolsM1}
* Full name: {@link (WellknownSymbolsM1:namespace)}
*/
export namespace WellknownSymbolsM1 {
/**
* Shortest name: {@link WellknownSymbolsM1.toStringPrimitive}
* Full name: {@link (WellknownSymbolsM1:namespace).(toStringPrimitive:variable)}
*/
export const toStringPrimitive: unique symbol = Symbol();
/**
* Shortest name: {@link WellknownSymbolsM1.toNumberPrimitive}
* Full name: {@link (WellknownSymbolsM1:namespace).(toNumberPrimitive:variable)}
*/
export const toNumberPrimitive: unique symbol = Symbol();
}
/**
* Shortest name: {@link ClassM2}
* Full name: {@link (ClassM2:class)}
*/
export class ClassM2 {
/**
* Shortest name: {@link ClassM2.[WellknownSymbolsM1.toStringPrimitive]}
* Full name: {@link (ClassM2:class).([(WellknownSymbolsM1:namespace).(toStringPrimitive:variable)]:instance)}
*/
public [WellknownSymbolsM1.toStringPrimitive](hint: string): string {
return '';
}
/**
* Shortest name: {@link ClassM2.([WellknownSymbolsM1.toNumberPrimitive]:instance)}
* Full name: {@link (ClassM2:class).([(WellknownSymbolsM1:namespace).(toNumberPrimitive:variable)]:instance)}
*/
public [WellknownSymbolsM1.toNumberPrimitive](hint: string): string {
return '';
}
/**
* Shortest name: {@link ClassM2.([WellknownSymbolsM1.toNumberPrimitive]:static)}
* Full name: {@link (ClassM2:class).([(WellknownSymbolsM1:namespace).(toNumberPrimitive:variable)]:static)}
*/
public static [WellknownSymbolsM1.toNumberPrimitive](hint: string): string {
return '';
}
}