Skip to content

Commit 0ed9247

Browse files
authored
Explicitly Omit unspreadable properties from rest type in the generic case (#47078)
1 parent 9b5abac commit 0ed9247

6 files changed

+1067
-7
lines changed

Diff for: src/compiler/checker.ts

+27-7
Original file line numberDiff line numberDiff line change
@@ -8464,8 +8464,32 @@ namespace ts {
84648464
if (source.flags & TypeFlags.Union) {
84658465
return mapType(source, t => getRestType(t, properties, symbol));
84668466
}
8467-
const omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName));
8467+
8468+
let omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName));
8469+
8470+
const spreadableProperties: Symbol[] = [];
8471+
const unspreadableToRestKeys: Type[] = [];
8472+
8473+
for (const prop of getPropertiesOfType(source)) {
8474+
const literalTypeFromProperty = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique);
8475+
if (!isTypeAssignableTo(literalTypeFromProperty, omitKeyType)
8476+
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
8477+
&& isSpreadableProperty(prop)) {
8478+
spreadableProperties.push(prop);
8479+
}
8480+
else {
8481+
unspreadableToRestKeys.push(literalTypeFromProperty);
8482+
}
8483+
}
8484+
84688485
if (isGenericObjectType(source) || isGenericIndexType(omitKeyType)) {
8486+
if (unspreadableToRestKeys.length) {
8487+
// If the type we're spreading from has properties that cannot
8488+
// be spread into the rest type (e.g. getters, methods), ensure
8489+
// they are explicitly omitted, as they would in the non-generic case.
8490+
omitKeyType = getUnionType([omitKeyType, ...unspreadableToRestKeys]);
8491+
}
8492+
84698493
if (omitKeyType.flags & TypeFlags.Never) {
84708494
return source;
84718495
}
@@ -8477,12 +8501,8 @@ namespace ts {
84778501
return getTypeAliasInstantiation(omitTypeAlias, [source, omitKeyType]);
84788502
}
84798503
const members = createSymbolTable();
8480-
for (const prop of getPropertiesOfType(source)) {
8481-
if (!isTypeAssignableTo(getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique), omitKeyType)
8482-
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
8483-
&& isSpreadableProperty(prop)) {
8484-
members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false));
8485-
}
8504+
for (const prop of spreadableProperties) {
8505+
members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false));
84868506
}
84878507
const result = createAnonymousType(symbol, members, emptyArray, emptyArray, getIndexInfosOfType(source));
84888508
result.objectFlags |= ObjectFlags.ObjectRestType;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(22,15): error TS2339: Property 'publicProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
2+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(23,15): error TS2339: Property 'publicProp' does not exist on type '{}'.
3+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(25,15): error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
4+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(26,15): error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
5+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(27,15): error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
6+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(28,15): error TS2339: Property 'privateProp' does not exist on type '{}'.
7+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(30,15): error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
8+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(31,15): error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
9+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(32,15): error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
10+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(33,15): error TS2339: Property 'protectedProp' does not exist on type '{}'.
11+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(35,15): error TS2339: Property 'getter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
12+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(36,15): error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
13+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(37,15): error TS2339: Property 'getter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
14+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(38,15): error TS2339: Property 'getter' does not exist on type '{}'.
15+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(40,15): error TS2339: Property 'setter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
16+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(41,15): error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
17+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(42,15): error TS2339: Property 'setter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
18+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(43,15): error TS2339: Property 'setter' does not exist on type '{}'.
19+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(45,15): error TS2339: Property 'method' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
20+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(46,15): error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
21+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(47,15): error TS2339: Property 'method' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
22+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(48,15): error TS2339: Property 'method' does not exist on type '{}'.
23+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(60,11): error TS2339: Property 'publicProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
24+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(61,11): error TS2339: Property 'publicProp' does not exist on type '{}'.
25+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(63,11): error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
26+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(64,11): error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
27+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(65,11): error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
28+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(66,11): error TS2339: Property 'privateProp' does not exist on type '{}'.
29+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(68,11): error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
30+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(69,11): error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
31+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(70,11): error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
32+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(71,11): error TS2339: Property 'protectedProp' does not exist on type '{}'.
33+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(73,11): error TS2339: Property 'getter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
34+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(74,11): error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
35+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(75,11): error TS2339: Property 'getter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
36+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(76,11): error TS2339: Property 'getter' does not exist on type '{}'.
37+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(78,11): error TS2339: Property 'setter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
38+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(79,11): error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
39+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(80,11): error TS2339: Property 'setter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
40+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(81,11): error TS2339: Property 'setter' does not exist on type '{}'.
41+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(83,11): error TS2339: Property 'method' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
42+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(84,11): error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
43+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(85,11): error TS2339: Property 'method' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
44+
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(86,11): error TS2339: Property 'method' does not exist on type '{}'.
45+
46+
47+
==== tests/cases/compiler/destructuringUnspreadableIntoRest.ts (44 errors) ====
48+
class A {
49+
constructor(
50+
public publicProp: string,
51+
private privateProp: string,
52+
protected protectedProp: string,
53+
) {}
54+
55+
get getter(): number {
56+
return 1;
57+
}
58+
59+
set setter(_v: number) {}
60+
61+
method() {
62+
const { ...rest1 } = this;
63+
const { ...rest2 } = this as A;
64+
const { publicProp: _1, ...rest3 } = this;
65+
const { publicProp: _2, ...rest4 } = this as A;
66+
67+
rest1.publicProp;
68+
rest2.publicProp;
69+
rest3.publicProp;
70+
~~~~~~~~~~
71+
!!! error TS2339: Property 'publicProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
72+
rest4.publicProp;
73+
~~~~~~~~~~
74+
!!! error TS2339: Property 'publicProp' does not exist on type '{}'.
75+
76+
rest1.privateProp;
77+
~~~~~~~~~~~
78+
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
79+
rest2.privateProp;
80+
~~~~~~~~~~~
81+
!!! error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
82+
rest3.privateProp;
83+
~~~~~~~~~~~
84+
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
85+
rest4.privateProp;
86+
~~~~~~~~~~~
87+
!!! error TS2339: Property 'privateProp' does not exist on type '{}'.
88+
89+
rest1.protectedProp;
90+
~~~~~~~~~~~~~
91+
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
92+
rest2.protectedProp;
93+
~~~~~~~~~~~~~
94+
!!! error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
95+
rest3.protectedProp;
96+
~~~~~~~~~~~~~
97+
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
98+
rest4.protectedProp;
99+
~~~~~~~~~~~~~
100+
!!! error TS2339: Property 'protectedProp' does not exist on type '{}'.
101+
102+
rest1.getter;
103+
~~~~~~
104+
!!! error TS2339: Property 'getter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
105+
rest2.getter;
106+
~~~~~~
107+
!!! error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
108+
rest3.getter;
109+
~~~~~~
110+
!!! error TS2339: Property 'getter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
111+
rest4.getter;
112+
~~~~~~
113+
!!! error TS2339: Property 'getter' does not exist on type '{}'.
114+
115+
rest1.setter;
116+
~~~~~~
117+
!!! error TS2339: Property 'setter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
118+
rest2.setter;
119+
~~~~~~
120+
!!! error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
121+
rest3.setter;
122+
~~~~~~
123+
!!! error TS2339: Property 'setter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
124+
rest4.setter;
125+
~~~~~~
126+
!!! error TS2339: Property 'setter' does not exist on type '{}'.
127+
128+
rest1.method;
129+
~~~~~~
130+
!!! error TS2339: Property 'method' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
131+
rest2.method;
132+
~~~~~~
133+
!!! error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
134+
rest3.method;
135+
~~~~~~
136+
!!! error TS2339: Property 'method' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
137+
rest4.method;
138+
~~~~~~
139+
!!! error TS2339: Property 'method' does not exist on type '{}'.
140+
}
141+
}
142+
143+
function destructure<T extends A>(x: T) {
144+
const { ...rest1 } = x;
145+
const { ...rest2 } = x as A;
146+
const { publicProp: _1, ...rest3 } = x;
147+
const { publicProp: _2, ...rest4 } = x as A;
148+
149+
rest1.publicProp;
150+
rest2.publicProp;
151+
rest3.publicProp;
152+
~~~~~~~~~~
153+
!!! error TS2339: Property 'publicProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
154+
rest4.publicProp;
155+
~~~~~~~~~~
156+
!!! error TS2339: Property 'publicProp' does not exist on type '{}'.
157+
158+
rest1.privateProp;
159+
~~~~~~~~~~~
160+
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
161+
rest2.privateProp;
162+
~~~~~~~~~~~
163+
!!! error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
164+
rest3.privateProp;
165+
~~~~~~~~~~~
166+
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
167+
rest4.privateProp;
168+
~~~~~~~~~~~
169+
!!! error TS2339: Property 'privateProp' does not exist on type '{}'.
170+
171+
rest1.protectedProp;
172+
~~~~~~~~~~~~~
173+
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
174+
rest2.protectedProp;
175+
~~~~~~~~~~~~~
176+
!!! error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
177+
rest3.protectedProp;
178+
~~~~~~~~~~~~~
179+
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
180+
rest4.protectedProp;
181+
~~~~~~~~~~~~~
182+
!!! error TS2339: Property 'protectedProp' does not exist on type '{}'.
183+
184+
rest1.getter;
185+
~~~~~~
186+
!!! error TS2339: Property 'getter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
187+
rest2.getter;
188+
~~~~~~
189+
!!! error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
190+
rest3.getter;
191+
~~~~~~
192+
!!! error TS2339: Property 'getter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
193+
rest4.getter;
194+
~~~~~~
195+
!!! error TS2339: Property 'getter' does not exist on type '{}'.
196+
197+
rest1.setter;
198+
~~~~~~
199+
!!! error TS2339: Property 'setter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
200+
rest2.setter;
201+
~~~~~~
202+
!!! error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
203+
rest3.setter;
204+
~~~~~~
205+
!!! error TS2339: Property 'setter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
206+
rest4.setter;
207+
~~~~~~
208+
!!! error TS2339: Property 'setter' does not exist on type '{}'.
209+
210+
rest1.method;
211+
~~~~~~
212+
!!! error TS2339: Property 'method' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
213+
rest2.method;
214+
~~~~~~
215+
!!! error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
216+
rest3.method;
217+
~~~~~~
218+
!!! error TS2339: Property 'method' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
219+
rest4.method;
220+
~~~~~~
221+
!!! error TS2339: Property 'method' does not exist on type '{}'.
222+
}
223+

0 commit comments

Comments
 (0)