Skip to content

Commit 667288b

Browse files
authored
Merge pull request #166 from Microsoft/reduceInstantiations2
Reduce symbol instantiations in lib.d.ts
2 parents 93c832c + c10531c commit 667288b

File tree

4 files changed

+1888
-2749
lines changed

4 files changed

+1888
-2749
lines changed

Shared.fsx

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ module JsonItems =
7070
| SignatureOverload
7171
| TypeDef
7272
| Extends
73-
override x.ToString() =
73+
override x.ToString() =
7474
match x with
7575
| Property _ -> "property"
7676
| Method _ -> "method"
@@ -205,7 +205,7 @@ type Param =
205205
/// Function overload
206206
type Overload =
207207
{ ParamCombinations : Param list
208-
ReturnTypes : string list
208+
ReturnTypes : string list
209209
Nullable : Boolean }
210210
member this.IsEmpty = this.ParamCombinations.IsEmpty && (this.ReturnTypes = [ "void" ] || this.ReturnTypes = [ "" ])
211211

@@ -320,8 +320,8 @@ let allCallbackFuncs =
320320

321321
let GetInterfaceByName = allInterfacesMap.TryFind
322322
let knownWorkerInterfaces =
323-
[ "Algorithm"; "AlgorithmIdentifier"; "KeyAlgorithm"; "CryptoKey"; "AbstractWorker"; "AudioBuffer"; "Blob";
324-
"CloseEvent"; "Console"; "Coordinates"; "DecodeSuccessCallback";
323+
[ "Algorithm"; "AlgorithmIdentifier"; "KeyAlgorithm"; "CryptoKey"; "AbstractWorker"; "AudioBuffer"; "Blob";
324+
"CloseEvent"; "Console"; "Coordinates"; "DecodeSuccessCallback";
325325
"DecodeErrorCallback"; "DOMError"; "DOMException"; "DOMStringList"; "ErrorEvent"; "Event"; "ErrorEventHandler";
326326
"EventException"; "EventInit"; "EventListener"; "EventTarget"; "File"; "FileList"; "FileReader";
327327
"FunctionStringCallback"; "IDBCursor"; "IDBCursorWithValue"; "IDBDatabase"; "IDBFactory"; "IDBIndex";
@@ -400,7 +400,7 @@ let getEventTypeInInterface eName iName =
400400
-> "Event"
401401
| "XMLHttpRequest", _
402402
-> "ProgressEvent"
403-
| _ ->
403+
| _ ->
404404
match eNameToEType.TryFind eName with
405405
| Some eType' -> eType'
406406
| _ -> "Event"
@@ -519,25 +519,31 @@ let iNameToEhList =
519519
else None)
520520
|> List.ofArray
521521
| None -> []
522+
if ownEventHandler.Length > 0 then ownEventHandler else []
522523

524+
allInterfaces
525+
|> Array.map (fun i -> (i.Name, GetEventHandler i))
526+
|> Map.ofArray
527+
528+
let iNameToEhParents =
529+
let hasHandler (i : Browser.Interface) =
530+
iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty
531+
532+
// Get all the event handlers from an interface and also from its inherited / implemented interfaces
533+
let rec GetEventHandler(i : Browser.Interface) =
523534
let extendedEventHandler =
524535
match GetInterfaceByName i.Extends with
525-
| Some i -> GetEventHandler i
526-
| None -> []
536+
| Some i when hasHandler i -> [i]
537+
| _ -> []
527538

528539
let implementedEventHandler =
529540
let implementis = i.Implements |> Array.map GetInterfaceByName
530541
[ for i' in implementis do
531542
yield! match i' with
532-
| Some i -> GetEventHandler i
543+
| Some i -> if hasHandler i then [i] else []
533544
| None -> [] ]
534545

535-
// Reason is if an interface doesn't have its own string overload for the addEventListener method,
536-
// the inherited overloads will be carried along; otherwise all of them will be overriten by its
537-
// own overloads, therefore re-declaration is needed
538-
if ownEventHandler.Length > 0 then
539-
List.concat [ ownEventHandler; extendedEventHandler; implementedEventHandler ]
540-
else []
546+
List.concat [ extendedEventHandler; implementedEventHandler ]
541547

542548
allInterfaces
543549
|> Array.map (fun i -> (i.Name, GetEventHandler i))
@@ -635,11 +641,11 @@ let GetOverloads (f : Function) (decomposeMultipleTypes : bool) =
635641
if decomposeMultipleTypes then
636642
[ for pComb in pCombList do
637643
yield { ParamCombinations = pComb
638-
ReturnTypes = rTypes
644+
ReturnTypes = rTypes
639645
Nullable = isNullable } ]
640646
else
641647
[ { ParamCombinations = getParams f
642-
ReturnTypes = rTypes
648+
ReturnTypes = rTypes
643649
Nullable = isNullable } ]
644650

645651
/// Define the subset of events that dedicated workers will use

TS.fsx

Lines changed: 90 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -117,32 +117,55 @@ let matchSingleParamMethodSignature (m: Browser.Method) expectedMName expectedMT
117117
/// Emit overloads for the createElement method
118118
let EmitCreateElementOverloads (m: Browser.Method) =
119119
if matchSingleParamMethodSignature m "createElement" "Element" "string" then
120-
for e in tagNameToEleName do
121-
if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then
122-
Pt.printl "createElement(tagName: \"%s\"): %s;" e.Key e.Value
120+
Pt.printl "createElement<K extends keyof HTMLElementTagNameMap>(tagName: K): HTMLElementTagNameMap[K];"
123121
Pt.printl "createElement(tagName: string): HTMLElement;"
124122

125123
/// Emit overloads for the getElementsByTagName method
126124
let EmitGetElementsByTagNameOverloads (m: Browser.Method) =
127125
if matchSingleParamMethodSignature m "getElementsByTagName" "NodeList" "string" then
128-
for e in tagNameToEleName do
129-
Pt.printl "getElementsByTagName(%s: \"%s\"): NodeListOf<%s>;" m.Params.[0].Name (e.Key.ToLower()) e.Value
126+
Pt.printl "getElementsByTagName<K extends keyof ElementListTagNameMap>(%s: K): ElementListTagNameMap[K];" m.Params.[0].Name
130127
Pt.printl "getElementsByTagName(%s: string): NodeListOf<Element>;" m.Params.[0].Name
131128

132129
/// Emit overloads for the querySelector method
133130
let EmitQuerySelectorOverloads (m: Browser.Method) =
134131
if matchSingleParamMethodSignature m "querySelector" "Element" "string" then
135-
for e in tagNameToEleName do
136-
Pt.printl "querySelector(selectors: \"%s\"): %s | null;" (e.Key.ToLower()) e.Value
132+
Pt.printl "querySelector<K extends keyof ElementTagNameMap>(selectors: K): ElementTagNameMap[K] | null;"
137133
Pt.printl "querySelector(selectors: string): Element | null;"
138134

139135
/// Emit overloads for the querySelectorAll method
140136
let EmitQuerySelectorAllOverloads (m: Browser.Method) =
141137
if matchSingleParamMethodSignature m "querySelectorAll" "NodeList" "string" then
142-
for e in tagNameToEleName do
143-
Pt.printl "querySelectorAll(selectors: \"%s\"): NodeListOf<%s>;" (e.Key.ToLower()) e.Value
138+
Pt.printl "querySelectorAll<K extends keyof ElementListTagNameMap>(selectors: K): ElementListTagNameMap[K];"
144139
Pt.printl "querySelectorAll(selectors: string): NodeListOf<Element>;"
145140

141+
let EmitHTMLElementTagNameMap () =
142+
Pt.printl "interface HTMLElementTagNameMap {"
143+
Pt.increaseIndent()
144+
for e in tagNameToEleName do
145+
if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then
146+
Pt.printl "\"%s\": %s;" (e.Key.ToLower()) e.Value
147+
Pt.decreaseIndent()
148+
Pt.printl "}"
149+
Pt.printl ""
150+
151+
let EmitElementTagNameMap () =
152+
Pt.printl "interface ElementTagNameMap {"
153+
Pt.increaseIndent()
154+
for e in tagNameToEleName do
155+
Pt.printl "\"%s\": %s;" (e.Key.ToLower()) e.Value
156+
Pt.decreaseIndent()
157+
Pt.printl "}"
158+
Pt.printl ""
159+
160+
let EmitElementListTagNameMap () =
161+
Pt.printl "interface ElementListTagNameMap {"
162+
Pt.increaseIndent()
163+
for e in tagNameToEleName do
164+
Pt.printl "\"%s\": NodeListOf<%s>;" (e.Key.ToLower()) e.Value
165+
Pt.decreaseIndent()
166+
Pt.printl "}"
167+
Pt.printl ""
168+
146169
/// Emit overloads for the createEvent method
147170
let EmitCreateEventOverloads (m: Browser.Method) =
148171
if matchSingleParamMethodSignature m "createEvent" "Event" "string" then
@@ -205,7 +228,7 @@ let EmitMethod flavor prefix (i:Browser.Interface) (m:Browser.Method) =
205228
let overloads = GetOverloads (Function.Method m) false
206229
for { ParamCombinations = pCombList; ReturnTypes = rTypes; Nullable = isNullable } in overloads do
207230
let paramsString = ParamsToString pCombList
208-
let returnString =
231+
let returnString =
209232
let returnType = rTypes |> List.map DomTypeToTsType |> String.concat " | "
210233
if isNullable then makeNullable returnType else returnType
211234
Pt.printl "%s%s(%s): %s;" prefix (if m.Name.IsSome then m.Name.Value else "") paramsString returnString
@@ -243,12 +266,11 @@ let EmitEnums () =
243266
let emitEnum (e: Browser.Enum) = Pt.printl "declare var %s: string;" e.Name
244267
browser.Enums |> Array.iter emitEnum
245268

246-
let EmitEventHandlerThis flavor (prefix: string) =
247-
if prefix = "" then "this: this, "
269+
let EmitEventHandlerThis flavor (prefix: string) (i: Browser.Interface) =
270+
if prefix = "" then "this: " + i.Name + ", "
248271
else match GetGlobalPollutor flavor with
249272
| Some pollutor -> "this: " + pollutor.Name + ", "
250273
| _ -> ""
251-
252274
let EmitProperties flavor prefix (emitScope: EmitScope) (i: Browser.Interface)=
253275
let emitPropertyFromJson (p: ItemsType.Root) =
254276
let readOnlyModifier =
@@ -272,15 +294,15 @@ let EmitProperties flavor prefix (emitScope: EmitScope) (i: Browser.Interface)=
272294
let pType =
273295
match p.Type with
274296
| "EventHandler" ->
275-
// Sometimes event handlers with the same name may actually handle different
276-
// events in different interfaces. For example, "onerror" handles "ErrorEvent"
297+
// Sometimes event handlers with the same name may actually handle different
298+
// events in different interfaces. For example, "onerror" handles "ErrorEvent"
277299
// normally, but in "SVGSVGElement" it handles "SVGError" event instead.
278-
let eType =
300+
let eType =
279301
if p.EventHandler.IsSome then
280302
getEventTypeInInterface p.EventHandler.Value i.Name
281-
else
303+
else
282304
"Event"
283-
String.Format("({0}ev: {1}) => any", EmitEventHandlerThis flavor prefix, eType)
305+
String.Format("({0}ev: {1}) => any", EmitEventHandlerThis flavor prefix i, eType)
284306
| _ -> DomTypeToTsType p.Type
285307
let pTypeAndNull = if p.Nullable.IsSome then makeNullable pType else pType
286308
let readOnlyModifier = if p.ReadOnly.IsSome && prefix = "" then "readonly " else ""
@@ -307,15 +329,11 @@ let EmitMethods flavor prefix (emitScope: EmitScope) (i: Browser.Interface) =
307329
let emitMethodFromJson (m: ItemsType.Root) =
308330
m.Signatures |> Array.iter (Pt.printl "%s%s;" prefix)
309331

310-
// Because eventhandler overload are not inherited between interfaces,
311-
// they need to be taken care of seperately
312-
let hasEventHandlers =
313-
iNameToEhList.ContainsKey i.Name &&
314-
not iNameToEhList.[i.Name].IsEmpty
315-
332+
// If prefix is not empty, then this is the global declare function addEventListener, we want to override this
333+
// Otherwise, this is EventTarget.addEventListener, we want to keep that.
316334
let mFilter (m:Browser.Method) =
317335
matchScope emitScope m &&
318-
not (hasEventHandlers && OptionCheckValue "addEventListener" m.Name)
336+
not (prefix <> "" && OptionCheckValue "addEventListener" m.Name)
319337

320338
if i.Methods.IsSome then
321339
i.Methods.Value.Methods
@@ -349,36 +367,30 @@ let rec EmitAllMembers flavor (i:Browser.Interface) =
349367
| _ -> ()
350368

351369
let EmitEventHandlers (flavor: Flavor) (prefix: string) (i:Browser.Interface) =
352-
let emitEventHandler prefix (eHandler: EventHandler) =
353-
let eventType =
354-
getEventTypeInInterface eHandler.EventName i.Name
370+
let fPrefix =
371+
if prefix.StartsWith "declare var" then "declare function " else ""
372+
373+
let emitEventHandler prefix (i:Browser.Interface) =
355374
Pt.printl
356-
"%saddEventListener(type: \"%s\", listener: (%sev: %s) => any, useCapture?: boolean): void;"
357-
prefix eHandler.EventName (EmitEventHandlerThis flavor prefix) eventType
358-
359-
let fPrefix = if prefix.StartsWith "declare var" then "declare function " else ""
360-
361-
// Inheritance of "addEventListener" has two cases:
362-
// 1. No own eventhandlers -> it inherits all the eventhandlers from base interfaces
363-
// 2. Has own eventhandlers -> TypeScript's inherit mechanism erases all inherited eventhandler overloads
364-
// so they need to be reprinted.
365-
if iNameToEhList.ContainsKey i.Name then
366-
iNameToEhList.[i.Name] |> List.sortBy (fun eh -> eh.EventName) |> List.iter (emitEventHandler fPrefix)
367-
let shouldPrintAddEventListener =
368-
if iNameToEhList.[i.Name].Length > 0 then true
369-
else
370-
match i.Extends, i.Implements.Length with
371-
| _, 0 -> false
372-
| "Object", 1 -> false
373-
| _ ->
374-
let allParents = Array.append [|i.Extends|] i.Implements
375-
match allParents |> Array.filter iNameToEhList.ContainsKey |> Array.length with
376-
// only one of the implemented interface has EventHandlers
377-
| 0 | 1 -> false
378-
// multiple implemented interfaces have EventHandlers
379-
| _ -> true
380-
if shouldPrintAddEventListener then
381-
Pt.printl "%saddEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;" fPrefix
375+
"%saddEventListener<K extends keyof %sEventMap>(type: K, listener: (this: %s, ev: %sEventMap[K]) => any, useCapture?: boolean): void;"
376+
prefix i.Name i.Name i.Name
377+
378+
let shouldEmitStringEventHandler =
379+
if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then
380+
emitEventHandler fPrefix i
381+
true
382+
elif iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then
383+
iNameToEhParents.[i.Name]
384+
|> List.sortBy (fun i -> i.Name)
385+
|> List.iter (emitEventHandler fPrefix)
386+
true
387+
else
388+
false
389+
390+
if shouldEmitStringEventHandler then
391+
Pt.printl
392+
"%saddEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;"
393+
fPrefix
382394

383395
let EmitConstructorSignature (i:Browser.Interface) =
384396
let emitConstructorSigFromJson (c: ItemsType.Root) =
@@ -430,7 +442,7 @@ let EmitNamedConstructors () =
430442

431443
let EmitInterfaceDeclaration (i:Browser.Interface) =
432444
Pt.printl "interface %s" i.Name
433-
let finalExtends =
445+
let finalExtends =
434446
let overridenExtendsFromJson =
435447
JsonItems.getOverriddenItemsByInterfaceName ItemKind.Extends Flavor.All i.Name
436448
|> Array.map (fun e -> e.BaseInterface.Value) |> List.ofArray
@@ -516,7 +528,27 @@ let EmitIndexers emitScope (i: Browser.Interface) =
516528
|> Array.filter (matchInterface i.Name)
517529
|> Array.iter emitIndexerFromJson
518530

531+
let EmitInterfaceEventMap flavor (i:Browser.Interface) =
532+
let EmitInterfaceEventMapEntry (eHandler: EventHandler) =
533+
let eventType =
534+
getEventTypeInInterface eHandler.EventName i.Name
535+
Pt.printl "\"%s\": %s;" eHandler.EventName eventType
536+
537+
let ownEventHandles = if iNameToEhList.ContainsKey i.Name && not iNameToEhList.[i.Name].IsEmpty then iNameToEhList.[i.Name] else []
538+
if ownEventHandles.Length > 0 then
539+
Pt.printl "interface %sEventMap" i.Name
540+
if iNameToEhParents.ContainsKey i.Name && not iNameToEhParents.[i.Name].IsEmpty then
541+
let extends = iNameToEhParents.[i.Name] |> List.map (fun i -> i.Name + "EventMap")
542+
Pt.print " extends %s" (String.Join(", ", extends))
543+
Pt.print " {"
544+
Pt.increaseIndent()
545+
ownEventHandles |> List.iter EmitInterfaceEventMapEntry
546+
Pt.decreaseIndent()
547+
Pt.printl "}"
548+
Pt.printl ""
519549
let EmitInterface flavor (i:Browser.Interface) =
550+
EmitInterfaceEventMap flavor i
551+
520552
Pt.resetIndent()
521553
EmitInterfaceDeclaration i
522554
Pt.increaseIndent()
@@ -718,6 +750,9 @@ let EmitTheWholeThing flavor (target:TextWriter) =
718750
EmitCallBackFunctions flavor
719751

720752
if flavor <> Worker then
753+
EmitHTMLElementTagNameMap()
754+
EmitElementTagNameMap()
755+
EmitElementListTagNameMap()
721756
EmitNamedConstructors()
722757

723758
match GetGlobalPollutor flavor with

0 commit comments

Comments
 (0)