Skip to content

Commit

Permalink
Explicitly Omit unspreadable properties from rest type in the generic…
Browse files Browse the repository at this point in the history
… case (microsoft#47078)
  • Loading branch information
jakebailey authored and mprobst committed Jan 10, 2022
1 parent 87d083f commit 426f643
Show file tree
Hide file tree
Showing 6 changed files with 1,067 additions and 7 deletions.
34 changes: 27 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8464,8 +8464,32 @@ namespace ts {
if (source.flags & TypeFlags.Union) {
return mapType(source, t => getRestType(t, properties, symbol));
}
const omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName));

let omitKeyType = getUnionType(map(properties, getLiteralTypeFromPropertyName));

const spreadableProperties: Symbol[] = [];
const unspreadableToRestKeys: Type[] = [];

for (const prop of getPropertiesOfType(source)) {
const literalTypeFromProperty = getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique);
if (!isTypeAssignableTo(literalTypeFromProperty, omitKeyType)
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
&& isSpreadableProperty(prop)) {
spreadableProperties.push(prop);
}
else {
unspreadableToRestKeys.push(literalTypeFromProperty);
}
}

if (isGenericObjectType(source) || isGenericIndexType(omitKeyType)) {
if (unspreadableToRestKeys.length) {
// If the type we're spreading from has properties that cannot
// be spread into the rest type (e.g. getters, methods), ensure
// they are explicitly omitted, as they would in the non-generic case.
omitKeyType = getUnionType([omitKeyType, ...unspreadableToRestKeys]);
}

if (omitKeyType.flags & TypeFlags.Never) {
return source;
}
Expand All @@ -8477,12 +8501,8 @@ namespace ts {
return getTypeAliasInstantiation(omitTypeAlias, [source, omitKeyType]);
}
const members = createSymbolTable();
for (const prop of getPropertiesOfType(source)) {
if (!isTypeAssignableTo(getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique), omitKeyType)
&& !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected))
&& isSpreadableProperty(prop)) {
members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false));
}
for (const prop of spreadableProperties) {
members.set(prop.escapedName, getSpreadSymbol(prop, /*readonly*/ false));
}
const result = createAnonymousType(symbol, members, emptyArray, emptyArray, getIndexInfosOfType(source));
result.objectFlags |= ObjectFlags.ObjectRestType;
Expand Down
223 changes: 223 additions & 0 deletions tests/baselines/reference/destructuringUnspreadableIntoRest.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(22,15): error TS2339: Property 'publicProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(23,15): error TS2339: Property 'publicProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(25,15): error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(26,15): error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(27,15): error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(28,15): error TS2339: Property 'privateProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(30,15): error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(31,15): error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(32,15): error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(33,15): error TS2339: Property 'protectedProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(35,15): error TS2339: Property 'getter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(36,15): error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(37,15): error TS2339: Property 'getter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(38,15): error TS2339: Property 'getter' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(40,15): error TS2339: Property 'setter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(41,15): error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(42,15): error TS2339: Property 'setter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(43,15): error TS2339: Property 'setter' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(45,15): error TS2339: Property 'method' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(46,15): error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(47,15): error TS2339: Property 'method' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(48,15): error TS2339: Property 'method' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(60,11): error TS2339: Property 'publicProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(61,11): error TS2339: Property 'publicProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(63,11): error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(64,11): error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(65,11): error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(66,11): error TS2339: Property 'privateProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(68,11): error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(69,11): error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(70,11): error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(71,11): error TS2339: Property 'protectedProp' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(73,11): error TS2339: Property 'getter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(74,11): error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(75,11): error TS2339: Property 'getter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(76,11): error TS2339: Property 'getter' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(78,11): error TS2339: Property 'setter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(79,11): error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(80,11): error TS2339: Property 'setter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(81,11): error TS2339: Property 'setter' does not exist on type '{}'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(83,11): error TS2339: Property 'method' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(84,11): error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(85,11): error TS2339: Property 'method' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
tests/cases/compiler/destructuringUnspreadableIntoRest.ts(86,11): error TS2339: Property 'method' does not exist on type '{}'.


==== tests/cases/compiler/destructuringUnspreadableIntoRest.ts (44 errors) ====
class A {
constructor(
public publicProp: string,
private privateProp: string,
protected protectedProp: string,
) {}

get getter(): number {
return 1;
}

set setter(_v: number) {}

method() {
const { ...rest1 } = this;
const { ...rest2 } = this as A;
const { publicProp: _1, ...rest3 } = this;
const { publicProp: _2, ...rest4 } = this as A;

rest1.publicProp;
rest2.publicProp;
rest3.publicProp;
~~~~~~~~~~
!!! error TS2339: Property 'publicProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.publicProp;
~~~~~~~~~~
!!! error TS2339: Property 'publicProp' does not exist on type '{}'.

rest1.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
rest2.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
rest3.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type '{}'.

rest1.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
rest2.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
rest3.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type '{}'.

rest1.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
rest2.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
rest3.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type '{}'.

rest1.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
rest2.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
rest3.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type '{}'.

rest1.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type 'Omit<this, "getter" | "setter" | "method">'.
rest2.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
rest3.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type 'Omit<this, "publicProp" | "getter" | "setter" | "method">'.
rest4.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type '{}'.
}
}

function destructure<T extends A>(x: T) {
const { ...rest1 } = x;
const { ...rest2 } = x as A;
const { publicProp: _1, ...rest3 } = x;
const { publicProp: _2, ...rest4 } = x as A;

rest1.publicProp;
rest2.publicProp;
rest3.publicProp;
~~~~~~~~~~
!!! error TS2339: Property 'publicProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.publicProp;
~~~~~~~~~~
!!! error TS2339: Property 'publicProp' does not exist on type '{}'.

rest1.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
rest2.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type '{ publicProp: string; }'.
rest3.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.privateProp;
~~~~~~~~~~~
!!! error TS2339: Property 'privateProp' does not exist on type '{}'.

rest1.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
rest2.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type '{ publicProp: string; }'.
rest3.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.protectedProp;
~~~~~~~~~~~~~
!!! error TS2339: Property 'protectedProp' does not exist on type '{}'.

rest1.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
rest2.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type '{ publicProp: string; }'.
rest3.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.getter;
~~~~~~
!!! error TS2339: Property 'getter' does not exist on type '{}'.

rest1.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
rest2.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type '{ publicProp: string; }'.
rest3.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.setter;
~~~~~~
!!! error TS2339: Property 'setter' does not exist on type '{}'.

rest1.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type 'Omit<T, "getter" | "setter" | "method">'.
rest2.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type '{ publicProp: string; }'.
rest3.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type 'Omit<T, "publicProp" | "getter" | "setter" | "method">'.
rest4.method;
~~~~~~
!!! error TS2339: Property 'method' does not exist on type '{}'.
}

Loading

0 comments on commit 426f643

Please sign in to comment.