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