Skip to content

Commit 889812b

Browse files
committed
Use fake scopes for type parameters too, and other needed changes
1 parent b6c72c1 commit 889812b

10 files changed

+83
-52
lines changed

src/compiler/checker.ts

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6623,8 +6623,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
66236623
}
66246624
if (
66256625
context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
6626-
type.flags & TypeFlags.TypeParameter &&
6627-
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)
6626+
type.flags & TypeFlags.TypeParameter
66286627
) {
66296628
const name = typeParameterToName(type, context);
66306629
context.approximateLength += idText(name).length;
@@ -7527,7 +7526,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
75277526
&& signature.declaration
75287527
&& signature.declaration !== context.enclosingDeclaration
75297528
&& !isInJSFile(signature.declaration)
7530-
&& some(expandedParams)
7529+
&& (some(expandedParams) || some(signature.typeParameters))
75317530
) {
75327531
// As a performance optimization, reuse the same fake scope within this chain.
75337532
// This is especially needed when we are working on an excessively deep type;
@@ -7545,32 +7544,64 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
75457544
// Note that we only check the most immediate enclosingDeclaration; the only place we
75467545
// could potentially add another fake scope into the chain is right here, so we don't
75477546
// traverse all ancestors.
7548-
const existingFakeScope = getNodeLinks(context.enclosingDeclaration).fakeScopeForSignatureDeclaration ? context.enclosingDeclaration : undefined;
7549-
Debug.assertOptionalNode(existingFakeScope, isBlock);
7547+
pushFakeScope(
7548+
"params",
7549+
add => {
7550+
for (const param of expandedParams) {
7551+
add(param.escapedName, param);
7552+
}
7553+
},
7554+
);
75507555

7551-
const locals = existingFakeScope?.locals ?? createSymbolTable();
7556+
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
7557+
// TODO(jakebailey): should this instead be done before walking type parameters?
7558+
pushFakeScope(
7559+
"typeParams",
7560+
add => {
7561+
for (const typeParam of signature.typeParameters ?? emptyArray) {
7562+
const typeParamName = typeParameterToName(typeParam, context).escapedText;
7563+
add(typeParamName, typeParam.symbol);
7564+
}
7565+
},
7566+
);
7567+
}
75527568

7553-
let newLocals: __String[] | undefined;
7554-
for (const param of expandedParams) {
7555-
if (!locals.has(param.escapedName)) {
7556-
newLocals = append(newLocals, param.escapedName);
7557-
locals.set(param.escapedName, param);
7569+
function pushFakeScope(kind: "params" | "typeParams", addAll: (addSymbol: (name: __String, symbol: Symbol) => void) => void) {
7570+
// We only ever need to look two declarations upward.
7571+
Debug.assert(context.enclosingDeclaration);
7572+
let existingFakeScope: Node | undefined;
7573+
if (getNodeLinks(context.enclosingDeclaration).fakeScopeForSignatureDeclaration === kind) {
7574+
existingFakeScope = context.enclosingDeclaration;
75587575
}
7559-
}
7576+
else if (context.enclosingDeclaration.parent && getNodeLinks(context.enclosingDeclaration.parent).fakeScopeForSignatureDeclaration === kind) {
7577+
existingFakeScope = context.enclosingDeclaration.parent;
7578+
}
7579+
Debug.assertOptionalNode(existingFakeScope, isBlock);
75607580

7561-
if (newLocals) {
7562-
function removeNewLocals() {
7581+
const locals = existingFakeScope?.locals ?? createSymbolTable();
7582+
let newLocals: __String[] | undefined;
7583+
addAll((name, symbol) => {
7584+
if (!locals.has(name)) {
7585+
newLocals = append(newLocals, name);
7586+
locals.set(name, symbol);
7587+
}
7588+
});
7589+
if (!newLocals) return;
7590+
7591+
const oldCleanup = cleanup;
7592+
function undo() {
75637593
forEach(newLocals, s => locals.delete(s));
7594+
oldCleanup?.();
75647595
}
75657596

75667597
if (existingFakeScope) {
7567-
cleanup = removeNewLocals;
7598+
cleanup = undo;
75687599
}
75697600
else {
75707601
// Use a Block for this; the type of the node doesn't matter so long as it
75717602
// has locals, and this is cheaper/easier than using a function-ish Node.
75727603
const fakeScope = parseNodeFactory.createBlock(emptyArray);
7573-
getNodeLinks(fakeScope).fakeScopeForSignatureDeclaration = true;
7604+
getNodeLinks(fakeScope).fakeScopeForSignatureDeclaration = kind;
75747605
fakeScope.locals = locals;
75757606

75767607
const saveEnclosingDeclaration = context.enclosingDeclaration;
@@ -7579,7 +7610,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
75797610

75807611
cleanup = () => {
75817612
context.enclosingDeclaration = saveEnclosingDeclaration;
7582-
removeNewLocals();
7613+
undo();
75837614
};
75847615
}
75857616
}
@@ -8127,13 +8158,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
81278158
}
81288159
}
81298160

8130-
function typeParameterShadowsNameInScope(escapedName: __String, context: NodeBuilderContext, type: TypeParameter) {
8161+
function typeParameterShadowsOtherTypeParameterInScope(escapedName: __String, context: NodeBuilderContext, type: TypeParameter) {
81318162
const result = resolveName(context.enclosingDeclaration, escapedName, SymbolFlags.Type, /*nameNotFoundMessage*/ undefined, escapedName, /*isUse*/ false);
8132-
if (result) {
8133-
if (result.flags & SymbolFlags.TypeParameter && result === type.symbol) {
8134-
return false;
8135-
}
8136-
return true;
8163+
if (result && result.flags & SymbolFlags.TypeParameter) {
8164+
return result !== type.symbol;
81378165
}
81388166
return false;
81398167
}
@@ -8153,7 +8181,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
81538181
const rawtext = result.escapedText as string;
81548182
let i = context.typeParameterNamesByTextNextNameCount?.get(rawtext) || 0;
81558183
let text = rawtext;
8156-
while (context.typeParameterNamesByText?.has(text) || typeParameterShadowsNameInScope(text as __String, context, type)) {
8184+
while (context.typeParameterNamesByText?.has(text) || typeParameterShadowsOtherTypeParameterInScope(text as __String, context, type)) {
81578185
i++;
81588186
text = `${rawtext}_${i}`;
81598187
}
@@ -8166,7 +8194,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
81668194
// `i` we've used thus far, to save work later
81678195
(context.typeParameterNamesByTextNextNameCount ||= new Map()).set(rawtext, i);
81688196
(context.typeParameterNames ||= new Map()).set(getTypeId(type), result);
8169-
(context.typeParameterNamesByText ||= new Set()).add(rawtext);
8197+
(context.typeParameterNamesByText ||= new Set()).add(text);
81708198
}
81718199
return result;
81728200
}
@@ -8345,7 +8373,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
83458373
}
83468374

83478375
function getEnclosingDeclarationIgnoringFakeScope(enclosingDeclaration: Node) {
8348-
return getNodeLinks(enclosingDeclaration).fakeScopeForSignatureDeclaration ? enclosingDeclaration.parent : enclosingDeclaration;
8376+
while (getNodeLinks(enclosingDeclaration).fakeScopeForSignatureDeclaration) {
8377+
enclosingDeclaration = enclosingDeclaration.parent;
8378+
}
8379+
return enclosingDeclaration;
83498380
}
83508381

83518382
/**
@@ -8425,7 +8456,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
84258456
}
84268457
if (isIdentifier(node)) {
84278458
const type = getDeclaredTypeOfSymbol(sym);
8428-
const name = sym.flags & SymbolFlags.TypeParameter && !isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration) ? typeParameterToName(type, context) : factory.cloneNode(node);
8459+
const name = sym.flags & SymbolFlags.TypeParameter ? typeParameterToName(type, context) : factory.cloneNode(node);
84298460
name.symbol = sym; // for quickinfo, which uses identifier symbol information
84308461
return { introducesError, node: setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping) };
84318462
}

src/compiler/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6047,7 +6047,7 @@ export interface NodeLinks {
60476047
decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime.
60486048
spreadIndices?: { first: number | undefined, last: number | undefined }; // Indices of first and last spread elements in array literal
60496049
parameterInitializerContainsUndefined?: boolean; // True if this is a parameter declaration whose type annotation contains "undefined".
6050-
fakeScopeForSignatureDeclaration?: boolean; // True if this is a fake scope injected into an enclosing declaration chain.
6050+
fakeScopeForSignatureDeclaration?: "params" | "typeParams"; // If present, this is a fake scope injected into an enclosing declaration chain.
60516051
assertionExpressionType?: Type; // Cached type of the expression of a type assertion
60526052
}
60536053

tests/baselines/reference/conditionalTypes1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,7 @@ type Foo<T> = T extends string ? boolean : number;
649649
type Bar<T> = T extends string ? boolean : number;
650650
declare const convert: <U>(value: Foo<U>) => Bar<U>;
651651
type Baz<T> = Foo<T>;
652-
declare const convert2: <T>(value: Foo<T>) => Foo<T>;
652+
declare const convert2: <T>(value: Foo<T>) => Baz<T>;
653653
declare function f31<T>(): void;
654654
declare function f32<T, U>(): void;
655655
declare function f33<T, U>(): void;

tests/baselines/reference/correlatedUnions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ type SameKeys<T> = {
602602
};
603603
};
604604
type MappedFromOriginal = SameKeys<Original>;
605-
declare const getStringAndNumberFromOriginalAndMapped: <K extends keyof Original, N extends keyof Original[K]>(original: Original, mappedFromOriginal: MappedFromOriginal, key: K, nestedKey: N) => [Original[K][N], SameKeys<Original>[K][N]];
605+
declare const getStringAndNumberFromOriginalAndMapped: <K extends keyof Original, N extends keyof Original[K]>(original: Original, mappedFromOriginal: MappedFromOriginal, key: K, nestedKey: N) => [Original[K][N], MappedFromOriginal[K][N]];
606606
interface Config {
607607
string: string;
608608
number: number;

tests/baselines/reference/declarationEmitDistributiveConditionalWithInfer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ exports.fun = fun;
1717

1818

1919
//// [declarationEmitDistributiveConditionalWithInfer.d.ts]
20-
export declare const fun: (subFun: <Collection, Field extends keyof Collection>() => (Collection[Field] extends infer T ? T extends Collection[Field] ? T extends readonly (infer InnerArr)[] ? InnerArr : T : never : never)[]) => void;
20+
export declare const fun: (subFun: <Collection, Field extends keyof Collection>() => FlatArray<Collection[Field], 0>[]) => void;

tests/baselines/reference/declarationEmitPrivatePromiseLikeInterface.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,6 @@ export interface HttpResponse<D extends unknown, E extends unknown = unknown> ex
7272
error: E;
7373
}
7474
export declare class HttpClient<SecurityDataType = unknown> {
75-
request: <T = any, E = any>() => TPromise<HttpResponse<T, E>, any>;
75+
request: <T = any, E = any>() => TPromise<HttpResponse<T, E>>;
7676
}
7777
export {};

tests/baselines/reference/declarationEmitReusesLambdaParameterNodes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ exports.CustomSelect2 = CustomSelect2;
2323

2424
//// [index.d.ts]
2525
import { Props } from "react-select";
26-
export declare const CustomSelect1: <Option>(x: Omit<import("react-select").Whatever, "y"> & Partial<import("react-select").Whatever> & Option) => void;
26+
export declare const CustomSelect1: <Option>(x: Props<Option> & {}) => void;
2727
export declare function CustomSelect2<Option>(x: Props<Option> & {}): void;

tests/baselines/reference/declarationEmitTypeParameterNameInOuterScope.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ function b2(x) { return x; }
3333
//// [declarationEmitTypeParameterNameInOuterScope.d.ts]
3434
declare class A {
3535
}
36-
declare var a: <A_1>(x: A_1) => A_1;
36+
declare var a: <A>(x: A) => A;
3737
declare function a2<A>(x: A): A;
38-
declare var a3: <A_1>(x: A_1) => A;
38+
declare var a3: <A>(x: A) => globalThis.A;
3939
declare function a4<A>(x: A): globalThis.A;
4040
interface B {
4141
}
42-
declare var b: <B_1>(x: B_1) => B_1;
42+
declare var b: <B>(x: B) => B;
4343
declare function b2<B>(x: B): B;

tests/baselines/reference/jsDeclarationsClasses.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,7 @@ export class F<T, U> {
541541
* @param {A} a
542542
* @param {B} b
543543
*/
544-
static create<A_1, B_1>(a: A_1, b: B_1): F<A_1, B_1>;
544+
static create<A, B>(a: A, b: B): F<A, B>;
545545
/**
546546
* @param {T} a
547547
* @param {U} b

tests/baselines/reference/jsDeclarationsInterfaces.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -134,19 +134,19 @@ export interface A {
134134
export interface B {
135135
cat: string;
136136
}
137-
export interface C<T_1, U_1> {
137+
export interface C<T, U> {
138138
new (): string;
139-
new (x: T_1): U_1;
140-
new <Q_4>(x: Q_4): T_1 & Q_4;
139+
new (x: T): U;
140+
new <Q_3>(x: Q_3): T & Q_3;
141141
(): number;
142-
(x: T_1): U_1;
143-
<Q_3>(x: Q_3): T_1 & Q_3;
144-
field: T_1 & U_1;
145-
optionalField?: T_1;
146-
readonly readonlyField: T_1 & U_1;
147-
readonly readonlyOptionalField?: U_1;
148-
method<Q_1 = number>(): number;
149-
method<Q_2>(a: T_1 & Q_2): Q_2 & number;
142+
(x: T): U;
143+
<Q_2>(x: Q_2): T & Q_2;
144+
field: T & U;
145+
optionalField?: T;
146+
readonly readonlyField: T & U;
147+
readonly readonlyOptionalField?: U;
148+
method<Q = number>(): number;
149+
method<Q_1>(a: T & Q_1): Q_1 & number;
150150
method(a?: number): number;
151151
method(...args: any[]): number;
152152
optMethod?(): number;
@@ -161,11 +161,11 @@ export interface K extends I, J {
161161
export interface L extends K {
162162
y: string;
163163
}
164-
export interface M<T_1> {
165-
field: T_1;
164+
export interface M<T> {
165+
field: T;
166166
}
167-
export interface N<U_1> extends M<U_1> {
168-
other: U_1;
167+
export interface N<U> extends M<U> {
168+
other: U;
169169
}
170170
export interface O {
171171
[idx: string]: string;

0 commit comments

Comments
 (0)