Skip to content

Commit a9f6ea6

Browse files
committed
Emit declarations using alias chains
1 parent d0d4067 commit a9f6ea6

5 files changed

+443
-16
lines changed

src/compiler/checker.ts

+36-16
Original file line numberDiff line numberDiff line change
@@ -5538,7 +5538,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
55385538
if (nodeIsSynthesized(importRef)) continue; // Synthetic names can't be resolved by `resolveExternalModuleName` - they'll cause a debug assert if they error
55395539
const resolvedModule = resolveExternalModuleName(enclosingDeclaration, importRef, /*ignoreErrors*/ true);
55405540
if (!resolvedModule) continue;
5541-
const ref = getAliasForSymbolInContainer(resolvedModule, symbol);
5541+
const ref = getAliasChainForSymbolInContainer(resolvedModule, symbol);
55425542
if (!ref) continue;
55435543
results = append(results, resolvedModule);
55445544
}
@@ -5555,7 +5555,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
55555555
for (const file of otherFiles) {
55565556
if (!isExternalModule(file)) continue;
55575557
const sym = getSymbolOfDeclaration(file);
5558-
const ref = getAliasForSymbolInContainer(sym, symbol);
5558+
const ref = getAliasChainForSymbolInContainer(sym, symbol);
55595559
if (!ref) continue;
55605560
results = append(results, sym);
55615561
}
@@ -5620,7 +5620,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
56205620
if (!length(candidates)) {
56215621
return undefined;
56225622
}
5623-
return mapDefined(candidates, candidate => getAliasForSymbolInContainer(candidate, symbol) ? candidate : undefined);
5623+
return mapDefined(candidates, candidate => getAliasChainForSymbolInContainer(candidate, symbol) ? candidate : undefined);
56245624

56255625
function fileSymbolIfFileSymbolExportEqualsContainer(d: Declaration) {
56265626
return container && getFileSymbolIfFileSymbolExportEqualsContainer(d, container);
@@ -5645,27 +5645,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
56455645
return exported && getSymbolIfSameReference(exported, container) ? fileSymbol : undefined;
56465646
}
56475647

5648-
function getAliasForSymbolInContainer(container: Symbol, symbol: Symbol) {
5648+
function getAliasChainForSymbolInContainer(container: Symbol, symbol: Symbol) {
56495649
if (container === getParentOfSymbol(symbol)) {
56505650
// fast path, `symbol` is either already the alias or isn't aliased
5651-
return symbol;
5651+
return [symbol];
56525652
}
56535653
// Check if container is a thing with an `export=` which points directly at `symbol`, and if so, return
56545654
// the container itself as the alias for the symbol
56555655
const exportEquals = container.exports && container.exports.get(InternalSymbolName.ExportEquals);
56565656
if (exportEquals && getSymbolIfSameReference(exportEquals, symbol)) {
5657-
return container;
5658-
}
5659-
const exports = getExportsOfSymbol(container);
5660-
const quick = exports.get(symbol.escapedName);
5661-
if (quick && getSymbolIfSameReference(quick, symbol)) {
5662-
return quick;
5657+
return [container];
56635658
}
5664-
return forEachEntry(exports, exported => {
5665-
if (getSymbolIfSameReference(exported, symbol)) {
5666-
return exported;
5659+
return getAliasChainRecursively(container);
5660+
5661+
function getAliasChainRecursively(container: Symbol): Symbol[] | undefined {
5662+
const exports = getExportsOfSymbol(container);
5663+
const quick = exports.get(symbol.escapedName);
5664+
if (quick && getSymbolIfSameReference(quick, symbol)) {
5665+
return [quick];
56675666
}
5668-
});
5667+
return forEachEntry(exports, exported => {
5668+
if (getSymbolIfSameReference(exported, symbol)) {
5669+
return [exported];
5670+
}
5671+
if (exported.flags & SymbolFlags.Alias) {
5672+
const aliasChain = getAliasChainRecursively(resolveAlias(exported));
5673+
if (aliasChain) {
5674+
aliasChain.unshift(exported);
5675+
return aliasChain;
5676+
}
5677+
}
5678+
});
5679+
}
56695680
}
56705681

56715682
/**
@@ -7792,7 +7803,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
77927803
accessibleSymbolChain = parentChain;
77937804
break;
77947805
}
7795-
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [getAliasForSymbolInContainer(parent, symbol) || symbol]);
7806+
if (accessibleSymbolChain) {
7807+
accessibleSymbolChain = parentChain.concat(accessibleSymbolChain);
7808+
break;
7809+
}
7810+
const aliasChain = getAliasChainForSymbolInContainer(parent, symbol);
7811+
if (aliasChain) {
7812+
accessibleSymbolChain = parentChain.concat(aliasChain);
7813+
break;
7814+
}
7815+
accessibleSymbolChain = parentChain.concat(symbol);
77967816
break;
77977817
}
77987818
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//// [tests/cases/compiler/declarationEmitUsingAliasSymbolChain1.ts] ////
2+
3+
//// [Data.d.ts]
4+
import type * as Types from "./Types.js";
5+
import type * as Equal from "./Equal.js";
6+
7+
export type Data<A extends Readonly<Record<string, any>> | ReadonlyArray<any>> =
8+
Readonly<A> & Equal.Equal;
9+
10+
export declare const TaggedClass: <Key extends string>(
11+
tag: Key,
12+
) => new <A extends Record<string, any>>(
13+
args: Types.Equals<Omit<A, keyof Equal.Equal>, {}> extends true
14+
? void
15+
: Omit<A, keyof Equal.Equal>,
16+
) => Data<
17+
A & {
18+
_tag: Key;
19+
}
20+
>;
21+
22+
//// [Equal.d.ts]
23+
import * as Hash from "./Hash.js";
24+
25+
export declare const symbol: unique symbol;
26+
27+
export interface Equal extends Hash.Hash {
28+
[symbol](that: Equal): boolean;
29+
}
30+
31+
//// [Hash.d.ts]
32+
export declare const symbol: unique symbol;
33+
34+
export interface Hash {
35+
[symbol](): number;
36+
}
37+
38+
//// [Types.d.ts]
39+
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
40+
T,
41+
>() => T extends Y ? 1 : 2
42+
? true
43+
: false;
44+
45+
//// [index.d.ts]
46+
export * as Data from "./Data.js";
47+
export * as Equal from "./Equal.js";
48+
export * as Types from "./Types.js";
49+
50+
//// [effect.cjs.d.ts]
51+
export * from "./declarations/src/index";
52+
53+
//// [package.json]
54+
{
55+
"name": "effect",
56+
"exports": {
57+
".": "./dist/effect.cjs.js"
58+
}
59+
}
60+
61+
//// [index.ts]
62+
import { Data } from "effect";
63+
export class Foo extends Data.TaggedClass("Foo")<{}> {}
64+
65+
//// [index.js]
66+
"use strict";
67+
var __extends = (this && this.__extends) || (function () {
68+
var extendStatics = function (d, b) {
69+
extendStatics = Object.setPrototypeOf ||
70+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
71+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
72+
return extendStatics(d, b);
73+
};
74+
return function (d, b) {
75+
if (typeof b !== "function" && b !== null)
76+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
77+
extendStatics(d, b);
78+
function __() { this.constructor = d; }
79+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
80+
};
81+
})();
82+
Object.defineProperty(exports, "__esModule", { value: true });
83+
exports.Foo = void 0;
84+
var effect_1 = require("effect");
85+
var Foo = /** @class */ (function (_super) {
86+
__extends(Foo, _super);
87+
function Foo() {
88+
return _super !== null && _super.apply(this, arguments) || this;
89+
}
90+
return Foo;
91+
}(effect_1.Data.TaggedClass("Foo")));
92+
exports.Foo = Foo;
93+
94+
95+
//// [index.d.ts]
96+
import { Data } from "effect";
97+
declare const Foo_base: new <A extends Record<string, any>>(args: import("effect").Types.Equals<Omit<A, keyof import("effect").Equal.Equal>, {}> extends true ? void : Omit<A, keyof import("effect").Equal.Equal>) => Data.Data<A & {
98+
_tag: "Foo";
99+
}>;
100+
export declare class Foo extends Foo_base<{}> {
101+
}
102+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//// [tests/cases/compiler/declarationEmitUsingAliasSymbolChain1.ts] ////
2+
3+
=== node_modules/effect/dist/declarations/src/Data.d.ts ===
4+
import type * as Types from "./Types.js";
5+
>Types : Symbol(Types, Decl(Data.d.ts, 0, 11))
6+
7+
import type * as Equal from "./Equal.js";
8+
>Equal : Symbol(Equal, Decl(Data.d.ts, 1, 11))
9+
10+
export type Data<A extends Readonly<Record<string, any>> | ReadonlyArray<any>> =
11+
>Data : Symbol(Data, Decl(Data.d.ts, 1, 41))
12+
>A : Symbol(A, Decl(Data.d.ts, 3, 17))
13+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
14+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
15+
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
16+
17+
Readonly<A> & Equal.Equal;
18+
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
19+
>A : Symbol(A, Decl(Data.d.ts, 3, 17))
20+
>Equal : Symbol(Equal, Decl(Data.d.ts, 1, 11))
21+
>Equal : Symbol(Equal.Equal, Decl(Equal.d.ts, 2, 43))
22+
23+
export declare const TaggedClass: <Key extends string>(
24+
>TaggedClass : Symbol(TaggedClass, Decl(Data.d.ts, 6, 20))
25+
>Key : Symbol(Key, Decl(Data.d.ts, 6, 35))
26+
27+
tag: Key,
28+
>tag : Symbol(tag, Decl(Data.d.ts, 6, 55))
29+
>Key : Symbol(Key, Decl(Data.d.ts, 6, 35))
30+
31+
) => new <A extends Record<string, any>>(
32+
>A : Symbol(A, Decl(Data.d.ts, 8, 10))
33+
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
34+
35+
args: Types.Equals<Omit<A, keyof Equal.Equal>, {}> extends true
36+
>args : Symbol(args, Decl(Data.d.ts, 8, 41))
37+
>Types : Symbol(Types, Decl(Data.d.ts, 0, 11))
38+
>Equals : Symbol(Types.Equals, Decl(Types.d.ts, 0, 0))
39+
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
40+
>A : Symbol(A, Decl(Data.d.ts, 8, 10))
41+
>Equal : Symbol(Equal, Decl(Data.d.ts, 1, 11))
42+
>Equal : Symbol(Equal.Equal, Decl(Equal.d.ts, 2, 43))
43+
44+
? void
45+
: Omit<A, keyof Equal.Equal>,
46+
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
47+
>A : Symbol(A, Decl(Data.d.ts, 8, 10))
48+
>Equal : Symbol(Equal, Decl(Data.d.ts, 1, 11))
49+
>Equal : Symbol(Equal.Equal, Decl(Equal.d.ts, 2, 43))
50+
51+
) => Data<
52+
>Data : Symbol(Data, Decl(Data.d.ts, 1, 41))
53+
54+
A & {
55+
>A : Symbol(A, Decl(Data.d.ts, 8, 10))
56+
57+
_tag: Key;
58+
>_tag : Symbol(_tag, Decl(Data.d.ts, 13, 7))
59+
>Key : Symbol(Key, Decl(Data.d.ts, 6, 35))
60+
}
61+
>;
62+
63+
=== node_modules/effect/dist/declarations/src/Equal.d.ts ===
64+
import * as Hash from "./Hash.js";
65+
>Hash : Symbol(Hash, Decl(Equal.d.ts, 0, 6))
66+
67+
export declare const symbol: unique symbol;
68+
>symbol : Symbol(symbol, Decl(Equal.d.ts, 2, 20))
69+
70+
export interface Equal extends Hash.Hash {
71+
>Equal : Symbol(Equal, Decl(Equal.d.ts, 2, 43))
72+
>Hash.Hash : Symbol(Hash.Hash, Decl(Hash.d.ts, 0, 43))
73+
>Hash : Symbol(Hash, Decl(Equal.d.ts, 0, 6))
74+
>Hash : Symbol(Hash.Hash, Decl(Hash.d.ts, 0, 43))
75+
76+
[symbol](that: Equal): boolean;
77+
>[symbol] : Symbol(Equal[symbol], Decl(Equal.d.ts, 4, 42))
78+
>symbol : Symbol(symbol, Decl(Equal.d.ts, 2, 20))
79+
>that : Symbol(that, Decl(Equal.d.ts, 5, 11))
80+
>Equal : Symbol(Equal, Decl(Equal.d.ts, 2, 43))
81+
}
82+
83+
=== node_modules/effect/dist/declarations/src/Hash.d.ts ===
84+
export declare const symbol: unique symbol;
85+
>symbol : Symbol(symbol, Decl(Hash.d.ts, 0, 20))
86+
87+
export interface Hash {
88+
>Hash : Symbol(Hash, Decl(Hash.d.ts, 0, 43))
89+
90+
[symbol](): number;
91+
>[symbol] : Symbol(Hash[symbol], Decl(Hash.d.ts, 2, 23))
92+
>symbol : Symbol(symbol, Decl(Hash.d.ts, 0, 20))
93+
}
94+
95+
=== node_modules/effect/dist/declarations/src/Types.d.ts ===
96+
export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
97+
>Equals : Symbol(Equals, Decl(Types.d.ts, 0, 0))
98+
>X : Symbol(X, Decl(Types.d.ts, 0, 19))
99+
>Y : Symbol(Y, Decl(Types.d.ts, 0, 21))
100+
>T : Symbol(T, Decl(Types.d.ts, 0, 29))
101+
>T : Symbol(T, Decl(Types.d.ts, 0, 29))
102+
>X : Symbol(X, Decl(Types.d.ts, 0, 19))
103+
104+
T,
105+
>T : Symbol(T, Decl(Types.d.ts, 0, 67))
106+
107+
>() => T extends Y ? 1 : 2
108+
>T : Symbol(T, Decl(Types.d.ts, 0, 67))
109+
>Y : Symbol(Y, Decl(Types.d.ts, 0, 21))
110+
111+
? true
112+
: false;
113+
114+
=== node_modules/effect/dist/declarations/src/index.d.ts ===
115+
export * as Data from "./Data.js";
116+
>Data : Symbol(Data, Decl(index.d.ts, 0, 6))
117+
118+
export * as Equal from "./Equal.js";
119+
>Equal : Symbol(Equal, Decl(index.d.ts, 1, 6))
120+
121+
export * as Types from "./Types.js";
122+
>Types : Symbol(Types, Decl(index.d.ts, 2, 6))
123+
124+
=== node_modules/effect/dist/effect.cjs.d.ts ===
125+
126+
export * from "./declarations/src/index";
127+
128+
=== src/index.ts ===
129+
import { Data } from "effect";
130+
>Data : Symbol(Data, Decl(index.ts, 0, 8))
131+
132+
export class Foo extends Data.TaggedClass("Foo")<{}> {}
133+
>Foo : Symbol(Foo, Decl(index.ts, 0, 30))
134+
>Data.TaggedClass : Symbol(Data.TaggedClass, Decl(Data.d.ts, 6, 20))
135+
>Data : Symbol(Data, Decl(index.ts, 0, 8))
136+
>TaggedClass : Symbol(Data.TaggedClass, Decl(Data.d.ts, 6, 20))
137+

0 commit comments

Comments
 (0)