Skip to content

Commit 6839973

Browse files
authored
Generate a unique type parameter name for each nested type parameter (#31544)
* Generate a unique type parameter name for each nested type parameter * Add testcase from 31605 * Fix typo * Liiiiiine eeeendingggggss
1 parent daf0a73 commit 6839973

5 files changed

+658
-18
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3699,14 +3699,10 @@ namespace ts {
36993699
return createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined));
37003700
}
37013701
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
3702-
type.flags & TypeFlags.TypeParameter &&
3703-
length(type.symbol.declarations) &&
3704-
isTypeParameterDeclaration(type.symbol.declarations[0]) &&
3705-
typeParameterShadowsNameInScope(type, context) &&
37063702
!isTypeSymbolAccessible(type.symbol, context.enclosingDeclaration)) {
3707-
const name = (type.symbol.declarations[0] as TypeParameterDeclaration).name;
3703+
const name = typeParameterToName(type, context);
37083704
context.approximateLength += idText(name).length;
3709-
return createTypeReferenceNode(getGeneratedNameForNode(name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes), /*typeArguments*/ undefined);
3705+
return createTypeReferenceNode(createIdentifier(idText(name)), /*typeArguments*/ undefined);
37103706
}
37113707
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
37123708
return type.symbol
@@ -4237,21 +4233,10 @@ namespace ts {
42374233
return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode, typeArguments);
42384234
}
42394235

4240-
function typeParameterShadowsNameInScope(type: TypeParameter, context: NodeBuilderContext) {
4241-
return !!resolveName(context.enclosingDeclaration, type.symbol.escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, type.symbol.escapedName, /*isUse*/ false);
4242-
}
4243-
42444236
function typeParameterToDeclarationWithConstraint(type: TypeParameter, context: NodeBuilderContext, constraintNode: TypeNode | undefined): TypeParameterDeclaration {
42454237
const savedContextFlags = context.flags;
42464238
context.flags &= ~NodeBuilderFlags.WriteTypeParametersInQualifiedName; // Avoids potential infinite loop when building for a claimspace with a generic
4247-
const shouldUseGeneratedName =
4248-
context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
4249-
type.symbol.declarations && type.symbol.declarations[0] &&
4250-
isTypeParameterDeclaration(type.symbol.declarations[0]) &&
4251-
typeParameterShadowsNameInScope(type, context);
4252-
const name = shouldUseGeneratedName
4253-
? getGeneratedNameForNode((type.symbol.declarations[0] as TypeParameterDeclaration).name, GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.ReservedInNestedScopes)
4254-
: symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
4239+
const name = typeParameterToName(type, context);
42554240
const defaultParameter = getDefaultFromTypeParameter(type);
42564241
const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context);
42574242
context.flags = savedContextFlags;
@@ -4584,6 +4569,35 @@ namespace ts {
45844569
}
45854570
}
45864571

4572+
function typeParameterShadowsNameInScope(escapedName: __String, context: NodeBuilderContext) {
4573+
return !!resolveName(context.enclosingDeclaration, escapedName, SymbolFlags.Type, /*nameNotFoundArg*/ undefined, escapedName, /*isUse*/ false);
4574+
}
4575+
4576+
function typeParameterToName(type: TypeParameter, context: NodeBuilderContext) {
4577+
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && context.typeParameterNames) {
4578+
const cached = context.typeParameterNames.get("" + getTypeId(type));
4579+
if (cached) {
4580+
return cached;
4581+
}
4582+
}
4583+
let result = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true);
4584+
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
4585+
const rawtext = result.escapedText as string;
4586+
let i = 0;
4587+
let text = rawtext;
4588+
while ((context.typeParameterNamesByText && context.typeParameterNamesByText.get(text)) || typeParameterShadowsNameInScope(text as __String, context)) {
4589+
i++;
4590+
text = `${rawtext}_${i}`;
4591+
}
4592+
if (text !== rawtext) {
4593+
result = createIdentifier(text, result.typeArguments);
4594+
}
4595+
(context.typeParameterNames || (context.typeParameterNames = createMap())).set("" + getTypeId(type), result);
4596+
(context.typeParameterNamesByText || (context.typeParameterNamesByText = createMap())).set(result.escapedText as string, true);
4597+
}
4598+
return result;
4599+
}
4600+
45874601
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier;
45884602
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName;
45894603
function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName {
@@ -4745,6 +4759,8 @@ namespace ts {
47454759
approximateLength: number;
47464760
truncating?: boolean;
47474761
typeParameterSymbolList?: Map<true>;
4762+
typeParameterNames?: Map<Identifier>;
4763+
typeParameterNamesByText?: Map<true>;
47484764
}
47494765

47504766
function isDefaultBindingContext(location: Node) {
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//// [declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.ts]
2+
// Note that both of the following have an `any` in their return type from where we bottom out the type printout
3+
// for having too many instances of the same symbol nesting.
4+
5+
// Slightly simplified repro from https://github.com/microsoft/TypeScript/issues/30732 so it's easier to read and debug
6+
export type Key<U> = keyof U;
7+
export type Value<K extends Key<U>, U> = U[K];
8+
export const updateIfChanged = <T>(t: T) => {
9+
const reduce = <U>(u: U, update: (u: U) => T) => {
10+
const set = (newU: U) => Object.is(u, newU) ? t : update(newU);
11+
return Object.assign(
12+
<K extends Key<U>>(key: K) =>
13+
reduce<Value<K, U>>(u[key as keyof U] as Value<K, U>, (v: Value<K, U>) => {
14+
return update(Object.assign(Array.isArray(u) ? [] : {}, u, { [key]: v }));
15+
}),
16+
{ map: (updater: (u: U) => U) => set(updater(u)), set });
17+
};
18+
return reduce<T>(t, (t: T) => t);
19+
};
20+
21+
// example from https://github.com/microsoft/TypeScript/issues/31605
22+
23+
export const testRecFun = <T extends Object>(parent: T) => {
24+
return {
25+
result: parent,
26+
deeper: <U extends Object>(child: U) =>
27+
testRecFun<T & U>({ ...parent, ...child })
28+
};
29+
}
30+
31+
32+
let p1 = testRecFun({ one: '1' })
33+
void p1.result.one;
34+
let p2 = p1.deeper({ two: '2' })
35+
void p2.result.one;
36+
void p2.result.two;
37+
let p3 = p2.deeper({ three: '3' })
38+
void p3.result.one;
39+
void p3.result.two;
40+
void p3.result.three;
41+
42+
43+
//// [declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.js]
44+
"use strict";
45+
// Note that both of the following have an `any` in their return type from where we bottom out the type printout
46+
// for having too many instances of the same symbol nesting.
47+
var __assign = (this && this.__assign) || function () {
48+
__assign = Object.assign || function(t) {
49+
for (var s, i = 1, n = arguments.length; i < n; i++) {
50+
s = arguments[i];
51+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
52+
t[p] = s[p];
53+
}
54+
return t;
55+
};
56+
return __assign.apply(this, arguments);
57+
};
58+
exports.__esModule = true;
59+
exports.updateIfChanged = function (t) {
60+
var reduce = function (u, update) {
61+
var set = function (newU) { return Object.is(u, newU) ? t : update(newU); };
62+
return Object.assign(function (key) {
63+
return reduce(u[key], function (v) {
64+
var _a;
65+
return update(Object.assign(Array.isArray(u) ? [] : {}, u, (_a = {}, _a[key] = v, _a)));
66+
});
67+
}, { map: function (updater) { return set(updater(u)); }, set: set });
68+
};
69+
return reduce(t, function (t) { return t; });
70+
};
71+
// example from https://github.com/microsoft/TypeScript/issues/31605
72+
exports.testRecFun = function (parent) {
73+
return {
74+
result: parent,
75+
deeper: function (child) {
76+
return exports.testRecFun(__assign({}, parent, child));
77+
}
78+
};
79+
};
80+
var p1 = exports.testRecFun({ one: '1' });
81+
void p1.result.one;
82+
var p2 = p1.deeper({ two: '2' });
83+
void p2.result.one;
84+
void p2.result.two;
85+
var p3 = p2.deeper({ three: '3' });
86+
void p3.result.one;
87+
void p3.result.two;
88+
void p3.result.three;
89+
90+
91+
//// [declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.d.ts]
92+
export declare type Key<U> = keyof U;
93+
export declare type Value<K extends Key<U>, U> = U[K];
94+
export declare const updateIfChanged: <T>(t: T) => (<K extends keyof T>(key: K) => (<K_1 extends keyof T[K]>(key: K_1) => (<K_2 extends keyof T[K][K_1]>(key: K_2) => (<K_3 extends keyof T[K][K_1][K_2]>(key: K_3) => (<K_4 extends keyof T[K][K_1][K_2][K_3]>(key: K_4) => (<K_5 extends keyof T[K][K_1][K_2][K_3][K_4]>(key: K_5) => (<K_6 extends keyof T[K][K_1][K_2][K_3][K_4][K_5]>(key: K_6) => (<K_7 extends keyof T[K][K_1][K_2][K_3][K_4][K_5][K_6]>(key: K_7) => (<K_8 extends keyof T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7]>(key: K_8) => (<K_9 extends keyof T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8]>(key: K_9) => (<K_10 extends keyof T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9]>(key: K_10) => any & {
95+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9][K_10]) => T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9][K_10]) => T;
96+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9][K_10]) => T;
97+
}) & {
98+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9]) => T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9]) => T;
99+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8][K_9]) => T;
100+
}) & {
101+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8]) => T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8]) => T;
102+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7][K_8]) => T;
103+
}) & {
104+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7]) => T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7]) => T;
105+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5][K_6][K_7]) => T;
106+
}) & {
107+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5][K_6]) => T[K][K_1][K_2][K_3][K_4][K_5][K_6]) => T;
108+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5][K_6]) => T;
109+
}) & {
110+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4][K_5]) => T[K][K_1][K_2][K_3][K_4][K_5]) => T;
111+
set: (newU: T[K][K_1][K_2][K_3][K_4][K_5]) => T;
112+
}) & {
113+
map: (updater: (u: T[K][K_1][K_2][K_3][K_4]) => T[K][K_1][K_2][K_3][K_4]) => T;
114+
set: (newU: T[K][K_1][K_2][K_3][K_4]) => T;
115+
}) & {
116+
map: (updater: (u: T[K][K_1][K_2][K_3]) => T[K][K_1][K_2][K_3]) => T;
117+
set: (newU: T[K][K_1][K_2][K_3]) => T;
118+
}) & {
119+
map: (updater: (u: T[K][K_1][K_2]) => T[K][K_1][K_2]) => T;
120+
set: (newU: T[K][K_1][K_2]) => T;
121+
}) & {
122+
map: (updater: (u: T[K][K_1]) => T[K][K_1]) => T;
123+
set: (newU: T[K][K_1]) => T;
124+
}) & {
125+
map: (updater: (u: T[K]) => T[K]) => T;
126+
set: (newU: T[K]) => T;
127+
}) & {
128+
map: (updater: (u: T) => T) => T;
129+
set: (newU: T) => T;
130+
};
131+
export declare const testRecFun: <T extends Object>(parent: T) => {
132+
result: T;
133+
deeper: <U extends Object>(child: U) => {
134+
result: T & U;
135+
deeper: <U_1 extends Object>(child: U_1) => {
136+
result: T & U & U_1;
137+
deeper: <U_2 extends Object>(child: U_2) => {
138+
result: T & U & U_1 & U_2;
139+
deeper: <U_3 extends Object>(child: U_3) => {
140+
result: T & U & U_1 & U_2 & U_3;
141+
deeper: <U_4 extends Object>(child: U_4) => {
142+
result: T & U & U_1 & U_2 & U_3 & U_4;
143+
deeper: <U_5 extends Object>(child: U_5) => {
144+
result: T & U & U_1 & U_2 & U_3 & U_4 & U_5;
145+
deeper: <U_6 extends Object>(child: U_6) => {
146+
result: T & U & U_1 & U_2 & U_3 & U_4 & U_5 & U_6;
147+
deeper: <U_7 extends Object>(child: U_7) => {
148+
result: T & U & U_1 & U_2 & U_3 & U_4 & U_5 & U_6 & U_7;
149+
deeper: <U_8 extends Object>(child: U_8) => {
150+
result: T & U & U_1 & U_2 & U_3 & U_4 & U_5 & U_6 & U_7 & U_8;
151+
deeper: <U_9 extends Object>(child: U_9) => {
152+
result: T & U & U_1 & U_2 & U_3 & U_4 & U_5 & U_6 & U_7 & U_8 & U_9;
153+
deeper: <U_10 extends Object>(child: U_10) => any;
154+
};
155+
};
156+
};
157+
};
158+
};
159+
};
160+
};
161+
};
162+
};
163+
};
164+
};

0 commit comments

Comments
 (0)