forked from microsoft/TypeScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeprecations.ts
152 lines (132 loc) · 5.78 KB
/
deprecations.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
import {
hasProperty,
UnionToIntersection,
Version,
} from "./_namespaces/ts.js";
import { deprecate } from "./deprecate.js";
/** @internal */
export interface DeprecationOptions {
message?: string;
error?: boolean;
since?: Version | string;
warnAfter?: Version | string;
errorAfter?: Version | string;
typeScriptVersion?: Version | string;
name?: string;
}
// The following are deprecations for the public API. Deprecated exports are removed from the compiler itself
// and compatible implementations are added here, along with an appropriate deprecation warning using
// the `@deprecated` JSDoc tag as well as the `deprecate` API.
//
// Deprecations fall into one of three categories:
//
// - "soft" - Soft deprecations are indicated with the `@deprecated` JSDoc Tag.
// - "warn" - Warning deprecations are indicated with the `@deprecated` JSDoc Tag and a diagnostic message (assuming a compatible host).
// - "error" - Error deprecations are either indicated with the `@deprecated` JSDoc tag and will throw a `TypeError` when invoked, or removed from the API entirely.
//
// 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.
/**
* Defines a list of overloads by ordinal
*
* @internal
*/
export type OverloadDefinitions = { readonly [P in number]: (...args: any[]) => any; };
/** A function that returns the ordinal of the overload that matches the provided arguments */
type OverloadBinder<T extends OverloadDefinitions> = (args: OverloadParameters<T>) => OverloadKeys<T> | undefined;
/**
* Extracts the ordinals from an set of overload definitions.
*
* @internal
*/
export type OverloadKeys<T extends OverloadDefinitions> = Extract<keyof T, number>;
/**
* Extracts a union of the potential parameter lists for each overload.
*
* @internal
*/
export type OverloadParameters<T extends OverloadDefinitions> = Parameters<{ [P in OverloadKeys<T>]: T[P]; }[OverloadKeys<T>]>;
// NOTE: the following doesn't work in TS 4.4 (the current LKG in main), so we have to use UnionToIntersection for now
// type OverloadFunction<T extends OverloadDefinitions, R extends ((...args: any[]) => any)[] = [], O = unknown> =
// R["length"] extends keyof T ? OverloadFunction<T, [...R, T[R["length"]]], O & T[R["length"]]> :
// unknown extends O ? never : O;
/**
* Constructs an intersection of each overload in a set of overload definitions.
*
* @internal
*/
export type OverloadFunction<T extends OverloadDefinitions> = UnionToIntersection<T[keyof T]>;
/**
* Maps each ordinal in a set of overload definitions to a function that can be used to bind its arguments.
*
* @internal
*/
export type OverloadBinders<T extends OverloadDefinitions> = { [P in OverloadKeys<T>]: (args: OverloadParameters<T>) => boolean | undefined; };
/**
* Defines deprecations for specific overloads by ordinal.
*
* @internal
*/
export type OverloadDeprecations<T extends OverloadDefinitions> = { [P in OverloadKeys<T>]?: DeprecationOptions; };
/** @internal */
export function createOverload<T extends OverloadDefinitions>(name: string, overloads: T, binder: OverloadBinders<T>, deprecations?: OverloadDeprecations<T>) {
Object.defineProperty(call, "name", { ...Object.getOwnPropertyDescriptor(call, "name"), value: name });
if (deprecations) {
for (const key of Object.keys(deprecations)) {
const index = +key as (keyof T & number);
if (!isNaN(index) && hasProperty(overloads, `${index}`)) {
overloads[index] = deprecate(overloads[index], { ...deprecations[index], name });
}
}
}
const bind = createBinder(overloads, binder);
return call as OverloadFunction<T>;
function call(...args: OverloadParameters<T>) {
const index = bind(args);
const fn = index !== undefined ? overloads[index] : undefined;
if (typeof fn === "function") {
return fn(...args);
}
throw new TypeError("Invalid arguments");
}
}
function createBinder<T extends OverloadDefinitions>(overloads: T, binder: OverloadBinders<T>): OverloadBinder<T> {
return args => {
for (let i = 0; hasProperty(overloads, `${i}`) && hasProperty(binder, `${i}`); i++) {
const fn = binder[i];
if (fn(args)) {
return i as OverloadKeys<T>;
}
}
};
}
/** @internal */
export interface OverloadBuilder {
overload<T extends OverloadDefinitions>(overloads: T): BindableOverloadBuilder<T>;
}
/** @internal */
export interface BindableOverloadBuilder<T extends OverloadDefinitions> {
bind(binder: OverloadBinders<T>): BoundOverloadBuilder<T>;
}
/** @internal */
export interface FinishableOverloadBuilder<T extends OverloadDefinitions> {
finish(): OverloadFunction<T>;
}
/** @internal */
export interface BoundOverloadBuilder<T extends OverloadDefinitions> extends FinishableOverloadBuilder<T> {
deprecate(deprecations: OverloadDeprecations<T>): FinishableOverloadBuilder<T>;
}
// NOTE: We only use this "builder" because we don't infer correctly when calling `createOverload` directly in < TS 4.7,
// but lib is currently at TS 4.4. We can switch to directly calling `createOverload` when we update LKG in main.
/** @internal */
export function buildOverload(name: string): OverloadBuilder {
return {
overload: overloads => ({
bind: binder => ({
finish: () => createOverload(name, overloads, binder),
deprecate: deprecations => ({
finish: () => createOverload(name, overloads, binder, deprecations),
}),
}),
}),
};
}