-
Notifications
You must be signed in to change notification settings - Fork 604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[api-extractor] Allow separate release tags for merged declarations #972
Comments
See discussion here: |
We should do this. A while ago I confirmed with the compiler people that the semantics are probably sound for .d.ts trimming of individual declarations. The implementation should be pretty easy as of AE7. |
I started looking at this again today. Turns out it introduces some complexities for API Extractor's warning regarding "incompatible release tags". The warning helps by catching situations like this: /** @beta */
interface I1 {
x: number;
}
// Incompatible release tag: the function f1() is marked as public, but its signature
// relies on the interface "I1" which is marked as beta. If the .d.ts rollup were to
// trim "I1", then callers of f1() won't be able to make the "i" parameter.
/** @public */
export function f1(i: I1): void { // <-- not okay
}
// But it's okay for the signature to reference a release tag that has greater visibility:
/** @alpha*/
export function g1(i: I1): void { // <-- okay because beta >= alpha
} When we allow different release tags for merged declarations, the analysis can get a little tricky: /** @beta */
export namespace N2 {
}
/** @public */
export interface N2 {
x: number;
}
// Even though N2:namespace is beta, it's okay here because
// this expression is only referring to N2:interface
/** @public */
export function f2(n: N2): void {
} And we get into trouble if we combine certain things: /** @public */
export interface I3 {
x: number;
}
// The two interfaces become the same semantic object, so it doesn't make sense
// for them to have different release tags. API Extractor must require both
// interface declarations to have the same release tag.
/** @beta*/ // <-- not okay
export interface I3 {
y: number;
} Since a TypeScript class can be used as an interface, the same problem affects classes that get merged with interfaces: /** @public */
export class C4 {
x: number;
}
/** @beta*/ // <-- not okay
export interface C4 {
y: number;
}
// If we trim C4:interface, then a caller cannot make a valid "c"
// (as an interface, not a class instance)
/** @public */
export function f4(c: C4): void {
} Here's another pathological case: /** @beta */
enum E5 {
X = 123
}
/** @public */
namespace E5 {
export type Y = "a" | "b";
}
/** @public */
function f5(e: E5.X | E5.Y): void { // <-- not okay
}
// This usage is possibly okay, but a nontrivial analysis is required
// because we don't know which "E5" we're referring to until we
// resolve "Y"
/** @public */
function g5(e: E5.Y): void {
} I feel like we can still implement this feature; it just requires a little more design thought. Rather than getting deep into type algebra, maybe we could identify a small set of cases that are obviously safe, and then slowly expand that set over time. Either way, this work item seems harder than I originally estimated, so we'll have to cut it from the initial release of AE7. |
@johnguy0 asked today about support for function overloads as a special case. For example: /** @beta */
export function add(x: string, y: string): string;
/** @public */
export function add(x: number, y: number): number;
// implementation
export function add(x: string|number, y: string|number): string|number {
} This seems relatively safe. |
Support for overloaded functions was published with API Extractor 7.5.0. I'm still open to supporting this for other types of declarations, but (as noted above) someone needs to propose some constraints for what is safe to allow. |
@octogonz - another thing I have in mind is union types. /**
* @public
*/
export interface IPublicInterface {}
/**
* @beta
*/
export interface IBetaInterface {}
/**
* @internal
*/
export interface IInternalInterface {}
/**
* This one one become
* - export type PublicType = IPublicInterface; for public release
* - export type PublicType = IPublicInterface | IBetaInterface; for beta release
* - export type PublicType = IPublicInterface | IBetaInterface | IInternalInterface; for internal
*
* @public
*/
export type PublicType = IPublicInterface | IBetaInterface | IInternalInterface; |
Reproduction
Export a merged type with two different release tags:
Expected
The function definition only shows up in beta rollup file
Actual
The function definition shows up in the public rollup file too, including the @beta tag
The text was updated successfully, but these errors were encountered: