1import { Debug, DeprecationOptions, hasProperty, UnionToIntersection } from "./_namespaces/ts"; 2 3// The following are deprecations for the public API. Deprecated exports are removed from the compiler itself 4// and compatible implementations are added here, along with an appropriate deprecation warning using 5// the `@deprecated` JSDoc tag as well as the `Debug.deprecate` API. 6// 7// Deprecations fall into one of three categories: 8// 9// - "soft" - Soft deprecations are indicated with the `@deprecated` JSDoc Tag. 10// - "warn" - Warning deprecations are indicated with the `@deprecated` JSDoc Tag and a diagnostic message (assuming a compatible host). 11// - "error" - Error deprecations are either indicated with the `@deprecated` JSDoc tag and will throw a `TypeError` when invoked, or removed from the API entirely. 12// 13// Once we have determined enough time has passed after a deprecation has been marked as `"warn"` or `"error"`, it will be removed from the public API. 14 15/** 16 * Defines a list of overloads by ordinal 17 * 18 * @internal 19 */ 20export type OverloadDefinitions = { readonly [P in number]: (...args: any[]) => any; }; 21 22/** A function that returns the ordinal of the overload that matches the provided arguments */ 23type OverloadBinder<T extends OverloadDefinitions> = (args: OverloadParameters<T>) => OverloadKeys<T> | undefined; 24 25/** 26 * Extracts the ordinals from an set of overload definitions. 27 * 28 * @internal 29 */ 30export type OverloadKeys<T extends OverloadDefinitions> = Extract<keyof T, number>; 31 32/** 33 * Extracts a union of the potential parameter lists for each overload. 34 * 35 * @internal 36 */ 37export type OverloadParameters<T extends OverloadDefinitions> = Parameters<{ [P in OverloadKeys<T>]: T[P]; }[OverloadKeys<T>]>; 38 39// NOTE: the following doesn't work in TS 4.4 (the current LKG in main), so we have to use UnionToIntersection for now 40// type OverloadFunction<T extends OverloadDefinitions, R extends ((...args: any[]) => any)[] = [], O = unknown> = 41// R["length"] extends keyof T ? OverloadFunction<T, [...R, T[R["length"]]], O & T[R["length"]]> : 42// unknown extends O ? never : O; 43/** 44 * Constructs an intersection of each overload in a set of overload definitions. 45 * 46 * @internal 47 */ 48export type OverloadFunction<T extends OverloadDefinitions> = UnionToIntersection<T[keyof T]>; 49 50/** 51 * Maps each ordinal in a set of overload definitions to a function that can be used to bind its arguments. 52 * 53 * @internal 54 */ 55export type OverloadBinders<T extends OverloadDefinitions> = { [P in OverloadKeys<T>]: (args: OverloadParameters<T>) => boolean | undefined; }; 56 57/** 58 * Defines deprecations for specific overloads by ordinal. 59 * 60 * @internal 61 */ 62export type OverloadDeprecations<T extends OverloadDefinitions> = { [P in OverloadKeys<T>]?: DeprecationOptions; }; 63 64/** @internal */ 65export function createOverload<T extends OverloadDefinitions>(name: string, overloads: T, binder: OverloadBinders<T>, deprecations?: OverloadDeprecations<T>) { 66 Object.defineProperty(call, "name", { ...Object.getOwnPropertyDescriptor(call, "name"), value: name }); 67 68 if (deprecations) { 69 for (const key of Object.keys(deprecations)) { 70 const index = +key as (keyof T & number); 71 if (!isNaN(index) && hasProperty(overloads, `${index}`)) { 72 overloads[index] = Debug.deprecate(overloads[index], { ...deprecations[index], name }); 73 } 74 } 75 } 76 77 const bind = createBinder(overloads, binder); 78 return call as OverloadFunction<T>; 79 80 function call(...args: OverloadParameters<T>) { 81 const index = bind(args); 82 const fn = index !== undefined ? overloads[index] : undefined; 83 if (typeof fn === "function") { 84 return fn(...args); 85 } 86 throw new TypeError("Invalid arguments"); 87 } 88} 89 90function createBinder<T extends OverloadDefinitions>(overloads: T, binder: OverloadBinders<T>): OverloadBinder<T> { 91 return args => { 92 for (let i = 0; hasProperty(overloads, `${i}`) && hasProperty(binder, `${i}`); i++) { 93 const fn = binder[i]; 94 if (fn(args)) { 95 return i as OverloadKeys<T>; 96 } 97 } 98 }; 99} 100 101/** @internal */ 102export interface OverloadBuilder { 103 overload<T extends OverloadDefinitions>(overloads: T): BindableOverloadBuilder<T>; 104} 105 106/** @internal */ 107export interface BindableOverloadBuilder<T extends OverloadDefinitions> { 108 bind(binder: OverloadBinders<T>): BoundOverloadBuilder<T>; 109} 110 111/** @internal */ 112export interface FinishableOverloadBuilder<T extends OverloadDefinitions> { 113 finish(): OverloadFunction<T>; 114} 115 116/** @internal */ 117export interface BoundOverloadBuilder<T extends OverloadDefinitions> extends FinishableOverloadBuilder<T> { 118 deprecate(deprecations: OverloadDeprecations<T>): FinishableOverloadBuilder<T>; 119} 120 121// NOTE: We only use this "builder" because we don't infer correctly when calling `createOverload` directly in < TS 4.7, 122// but lib is currently at TS 4.4. We can switch to directly calling `createOverload` when we update LKG in main. 123 124/** @internal */ 125export function buildOverload(name: string): OverloadBuilder { 126 return { 127 overload: overloads => ({ 128 bind: binder => ({ 129 finish: () => createOverload(name, overloads, binder), 130 deprecate: deprecations => ({ 131 finish: () => createOverload(name, overloads, binder, deprecations) 132 }) 133 }) 134 }) 135 }; 136}